{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "cperrin.xyz",
  "home_page_url": "https://staging.cperrin.xyz/en/",
  "feed_url": "https://staging.cperrin.xyz/en/index.json",
  "description": "A blog about sysadmin topics and more. Also a playground for CSS for me.",
  "authors": [
    {
      "name": "Christopher Perrin"
    }
  ],
  "items": [
    {
      "id": "https://staging.cperrin.xyz/en/2026/06/19/what-can-we-do-about-the-vibe-committer/",
      "url": "https://staging.cperrin.xyz/en/2026/06/19/what-can-we-do-about-the-vibe-committer/",
      "title": "What can we do about the vibe committer",
      "summary": "\u003cp\u003eI recently had a discussion on Mastodon that was sparked by a post of the Software Freedom Conservancy about the \u003ca href=\"https://sfconservancy.org/llm-gen-ai/llm-backed-generative-ai-recommendations.html\"\u003ehandling of LLM-gen-AI submissions\u003c/a\u003e. There are many people who are very opposed to letting LLM-generated code into their projects. This is an understandable standpoint given concerns like legality, unethical sourcing, resource usage, and concentration of power.\u003c/p\u003e\n\u003cp\u003eApart from that, it is completely reasonable for a maintainer to set rules for submissions. A FOSS license doesn\u0026rsquo;t mean that a maintainer even has to interact with anyone. It just provides the source.\u003c/p\u003e",
      "content_html": "\u003cp\u003eI recently had a discussion on Mastodon that was sparked by a post of the Software Freedom Conservancy about the \u003ca href=\"https://sfconservancy.org/llm-gen-ai/llm-backed-generative-ai-recommendations.html\"\u003ehandling of LLM-gen-AI submissions\u003c/a\u003e. There are many people who are very opposed to letting LLM-generated code into their projects. This is an understandable standpoint given concerns like legality, unethical sourcing, resource usage, and concentration of power.\u003c/p\u003e\n\u003cp\u003eApart from that, it is completely reasonable for a maintainer to set rules for submissions. A FOSS license doesn\u0026rsquo;t mean that a maintainer even has to interact with anyone. It just provides the source.\u003c/p\u003e\n\u003cp\u003eStill, it made me think about the impact these rules might have and in which cases they might turn into a net negative.\u003c/p\u003e\n\u003ch2 id=\"consent-is-king\"\u003eConsent is king\u003c/h2\u003e\n\u003cp\u003eMaintainers make the rules. They are entrusted with maintaining a version of the code that others might want to contribute to. Often that is a single person who not only made something they wanted to share, but also took it upon themselves to help others improve it. For this we should be grateful for their work and respect their wishes.\u003c/p\u003e\n\u003cp\u003eBut that is not how the world works. Through ignorance, entitlement, or malice, maintainers are often confronted with demands or hostility. Their boundaries get ignored, and frustrations rise. LLM-generated code is fertile ground for this. While some see it as the holy grail, others see it as the devil. This strong polarizing effect, combined with ignored boundaries, can easily lead to clashes.\u003c/p\u003e\n\u003ch2 id=\"solid-snake-and-benny-hill\"\u003eSolid Snake and Benny Hill\u003c/h2\u003e\n\u003cp\u003eEvery clash can be very stressful for all sides. This is especially true if innocent people get caught in the crossfire. In my years as a software developer I have seen a lot of bad code. More recently, some code has triggered my vibe code sensor, but it was written by a human.\u003c/p\u003e\n\u003cp\u003eOn the other hand, I believe I could probably hide the origin of a code submission. I would never do that, especially not to deceive. This makes me think that many people out there have the same knowledge and no problem with deception.\u003c/p\u003e\n\u003cp\u003eThis could lead to some people sneaking their LLM code in like Solid Snake into a secret base, and others being chased away by an angry maintainer like in a Benny Hill sketch.\u003c/p\u003e\n\u003ch2 id=\"distrust-leads-to-anger-anger-leads-to-a-bad-time-for-everyone\"\u003eDistrust leads to anger, anger leads to a bad time for everyone\u003c/h2\u003e\n\u003cp\u003eI am, of course, not the first person to think about nefarious actors trying to sneak past defenses. I am sure the most passionate maintainers are constantly aware of this, constantly on the lookout for the slightest hint of LLM involvement, always vigilant.\u003c/p\u003e\n\u003cp\u003eOr constantly concerned with the source of a submission in addition to the quality, always suspicious. From my observations, this can be coupled with anger - maybe anger about LLMs, about disregard of boundaries, or about the extra work they have to put in.\u003c/p\u003e\n\u003cp\u003eAt this point, it begs the question of who is winning anything here. Even more, some people are definitely losing. The maintainers, new programmers, and the FOSS community as a whole are negatively affected, while vibe coders and LLM companies still get what they want a lot of the time.\u003c/p\u003e\n\u003ch2 id=\"pragmatic-principles\"\u003ePragmatic principles\u003c/h2\u003e\n\u003cp\u003eThis all sounds very bad for people who have principles. Standing by principles might hurt someone innocent and even the maintainer, but abandoning principles might hurt just as much. My personal way out: pragmatic principles.\u003c/p\u003e\n\u003cp\u003eWhat I mean by that is searching specifically for the fights I can win and that are a net positive. Some of my guiding principles are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDon\u0026rsquo;t search for signs of LLM-generated code in a submission, but don\u0026rsquo;t ignore them\u003c/li\u003e\n\u003cli\u003eIf in doubt, either assume it\u0026rsquo;s human or communicate doubts\u003c/li\u003e\n\u003cli\u003eEvaluate submissions primarily on code quality\u003c/li\u003e\n\u003cli\u003eAssume people want to help\u003c/li\u003e\n\u003cli\u003eRemember that we all make mistakes\u003c/li\u003e\n\u003cli\u003eConcentrate on the positives\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"afterword\"\u003eAfterword\u003c/h2\u003e\n\u003cp\u003eI want to make it clear that I want everyone to set boundaries as they think right, and that everyone should respect those boundaries. I wish for a world where it is a given that the principles of others are respected.\u003c/p\u003e\n\u003cp\u003eThe pragmatic approach is only my way to cope when I can\u0026rsquo;t protect my principles in a way that is healthy or even successful. I am aware that this is not easy if a project is run over by slop. It\u0026rsquo;s not a complete solution and not for everyone.\u003c/p\u003e\n",
      "date_published": "2026-06-19T00:00:00+00:00",
      "date_modified": "2026-06-19T23:07:43+02:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2026/01/28/triggering-veeam-backups-on-linux-with-systemd/",
      "url": "https://staging.cperrin.xyz/en/2026/01/28/triggering-veeam-backups-on-linux-with-systemd/",
      "title": "Triggering Veeam Backups on Linux with SystemD",
      "summary": "\u003cp\u003eVeeam Backup is, in my opinion, one of the best free backup solutions available. The Linux version particularly shines with its ability to create snapshot backups of Ext4 partitions.\u003c/p\u003e\n\u003cp\u003eUnfortunately, the Linux version has a major drawback compared to its Windows counterpart: backup scheduling is extremely limited. You can only specify the day and hour for when backups should run. If the computer is asleep or powered off at that time, the backup won\u0026rsquo;t execute and, more importantly, won\u0026rsquo;t be rescheduled.\u003c/p\u003e",
      "content_html": "\u003cp\u003eVeeam Backup is, in my opinion, one of the best free backup solutions available. The Linux version particularly shines with its ability to create snapshot backups of Ext4 partitions.\u003c/p\u003e\n\u003cp\u003eUnfortunately, the Linux version has a major drawback compared to its Windows counterpart: backup scheduling is extremely limited. You can only specify the day and hour for when backups should run. If the computer is asleep or powered off at that time, the backup won\u0026rsquo;t execute and, more importantly, won\u0026rsquo;t be rescheduled.\u003c/p\u003e\n\u003cp\u003eTo work around this limitation, I had to get creative and combine SystemD timers, SystemD services, and some scripting.\u003c/p\u003e\n\u003ch2 id=\"veeam-cli\"\u003eVeeam CLI\u003c/h2\u003e\n\u003cp\u003eYou can also control Veeam jobs from outside the Terminal UI using the \u003ccode\u003eveeamconfig\u003c/code\u003e tool.\u003c/p\u003e\n\u003cp\u003eFor example, you can start a backup with the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eveeamconfig job run --name \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;JOBNAME\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe catch is that this command is non-blocking and returns immediately. To check if the backup has completed and its status, you need to run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eveeamconfig job status --name \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;JOBNAME\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince this command is also non-blocking, the only solution is polling. We need to repeatedly check if the job is still running until it either completes successfully or fails.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the key part of the polling script that handles the job monitoring:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Extract session ID from job start output\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eSESSION_ID\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$OUTPUT\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep -oP \u003cspan class=\"s1\"\u003e\u0026#39;Session ID: \\[\\{\\K[^}]+\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Poll session status until completion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e true\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eSTATUS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e/usr/bin/veeamconfig session info --id \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$SESSION_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep \u003cspan class=\"s2\"\u003e\u0026#34;State:\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eSTATE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATUS\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep -oP \u003cspan class=\"s1\"\u003e\u0026#39;State:\\s+\\K\\w+\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Success\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Warning\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Job completed with state: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eexit\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Failed\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Job failed!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eexit\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Job still running... State: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$STATE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    sleep \u003cspan class=\"nv\"\u003e$POLL_INTERVAL\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe script also includes signal handling to gracefully stop backups if needed.\u003c/p\u003e\n\u003ch2 id=\"systemd\"\u003eSystemD\u003c/h2\u003e\n\u003cp\u003eThe SystemD side was much more straightforward. SystemD timers allow me to wake the computer from sleep mode to start the backup. For this, you only need to set the option \u003ccode\u003eWakeSystem=true\u003c/code\u003e in the \u003ccode\u003e[Timer]\u003c/code\u003e block.\u003c/p\u003e\n\u003cp\u003eThe service unit was just as little of a problem with the script. I only had to prevent the computer from going back to sleep while the backup is running. For this, I used \u003ccode\u003esystemd-inhibit\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"systemd-service\"\u003eSystemD Service\u003c/h3\u003e\n\u003cp\u003eThe service unit runs our script while preventing the system from sleeping during backups:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eDescription\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003eVeeam Backup Job %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eAfter\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eType\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003esimple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eExecStart\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003esystemd-inhibit --what \u0026#34;sleep:idle:shutdown\u0026#34; --why \u0026#34;Veeam Backup %i\u0026#34; --who \u0026#34;Veeam Job\u0026#34; --mode \u0026#34;block-weak\u0026#34; /usr/local/bin/veeam-run-polling %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eRestart\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003eno\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"systemd-timer\"\u003eSystemD Timer\u003c/h3\u003e\n\u003cp\u003eThis timer wakes your system and runs the backup every other day at 10:30 AM:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eDescription\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003eVeeam Backup Timer for Job %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eOnCalendar\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e*-*-1/2 10:30:00\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003ePersistent\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eWakeSystem\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eDeferReactivation\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eUnit\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003eveeam-run@%i.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eWantedBy\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAll unit files and the script can be found at \u003ca href=\"https://git.screebo.net/cperrin/veeam-systemd-timers\"\u003ehttps://git.screebo.net/cperrin/veeam-systemd-timers\u003c/a\u003e.\u003c/p\u003e\n",
      "date_published": "2026-01-28T00:00:00+00:00",
      "date_modified": "2026-01-28T22:44:24+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2026/01/03/2026-the-year-windows-annoyed-us-too-much/",
      "url": "https://staging.cperrin.xyz/en/2026/01/03/2026-the-year-windows-annoyed-us-too-much/",
      "title": "2026: The Year Windows Annoyed Us Too Much",
      "summary": "\u003cp\u003eIn the last few days, I\u0026rsquo;ve read several blog posts talking about how 2026 is the year of the Linux desktop, and that\u0026rsquo;s a great thing. However, I think many of the headlines and posts on this topic should have a different title: \u0026ldquo;The Year Windows Annoyed Us Too Much.\u0026rdquo;\u003c/p\u003e\n\u003ch2 id=\"a-history-of-verschlimmbesserung\"\u003eA History of \u0026ldquo;\u003ca href=\"https://en.wiktionary.org/wiki/verschlimmbessern\"\u003eVerschlimmbesserung\u003c/a\u003e\u0026rdquo;\u003c/h2\u003e\n\u003cp\u003eFor years now, Microsoft has been steadily making its operating system worse through supposed \u0026ldquo;improvements\u0026rdquo; (\u003cem\u003eVerschlimmbesserung\u003c/em\u003e). Windows Vista was a difficult step because many computers weren\u0026rsquo;t ready for it yet, but there was at least technical progress that was successfully concluded with Windows 7.\u003c/p\u003e",
      "content_html": "\u003cp\u003eIn the last few days, I\u0026rsquo;ve read several blog posts talking about how 2026 is the year of the Linux desktop, and that\u0026rsquo;s a great thing. However, I think many of the headlines and posts on this topic should have a different title: \u0026ldquo;The Year Windows Annoyed Us Too Much.\u0026rdquo;\u003c/p\u003e\n\u003ch2 id=\"a-history-of-verschlimmbesserung\"\u003eA History of \u0026ldquo;\u003ca href=\"https://en.wiktionary.org/wiki/verschlimmbessern\"\u003eVerschlimmbesserung\u003c/a\u003e\u0026rdquo;\u003c/h2\u003e\n\u003cp\u003eFor years now, Microsoft has been steadily making its operating system worse through supposed \u0026ldquo;improvements\u0026rdquo; (\u003cem\u003eVerschlimmbesserung\u003c/em\u003e). Windows Vista was a difficult step because many computers weren\u0026rsquo;t ready for it yet, but there was at least technical progress that was successfully concluded with Windows 7.\u003c/p\u003e\n\u003cp\u003eOne step that many couldn\u0026rsquo;t follow was the switch to Windows 8. Then-CEO of Microsoft, Steve Ballmer, was convinced of making Windows a universally applicable operating system. Whether on a tablet, a phone, a desktop, or an entirely new device, Windows should be the right choice. In doing so, he created a system that served almost no one well. On the desktop, the full-screen interface constantly got in the way and changed the workflow. There were few tablets, and those that existed struggled with the fact that all worthwhile programs weren\u0026rsquo;t adapted for touch, and the system was way too heavy. On phones, the system never gained traction because no one developed apps for it, making it unattractive. Other unwelcome innovations of Windows 8 included the Microsoft account login, where users were forced to log in with an online Microsoft account instead of a local one.\u003c/p\u003e\n\u003cp\u003eSome of the design decisions of Windows 8 were then \u0026ldquo;partially reversed\u0026rdquo; with Windows 8.1. The mandatory full-screen start menu was removed, and the system became more usable again. Nevertheless, Microsoft was already starting with its next great idea: automatic syncing of documents to OneDrive. Instead of asking users, Microsoft simply assumed users would love it if their data just landed in the Microsoft cloud. Additionally, the free storage was too small for most people anyway. But then you can just pay for it.\u003c/p\u003e\n\u003ch2 id=\"the-forever-windows\"\u003eThe \u0026ldquo;Forever\u0026rdquo; Windows\u003c/h2\u003e\n\u003cp\u003eWindows 10 was then the \u0026ldquo;forever\u0026rdquo; Windows. It was supposed to be the last version of Windows and be continuously developed. The operating concept was now more classic again, but in many corners, things were strategically degraded. The start menu no longer just searched for programs but also documents and even online. Accordingly, it was slow and inaccurate. The tradition of pre-installed third-party software was continued with apps like Candy Crush and LinkedIn, which were automatically installed after a fresh installation of the system.\u003c/p\u003e\n\u003cp\u003eThe \u0026ldquo;improvements\u0026rdquo; came gradually: OneDrive was touted ever more aggressively, Edge made itself the default browser, more and more apps that nobody wanted appeared in the taskbar, and much more. Every major update brought some improvements and many downgrades.\u003c/p\u003e\n\u003ch2 id=\"windows-11-and-ai\"\u003eWindows 11 and AI\u003c/h2\u003e\n\u003cp\u003eA strong shift in mood toward Microsoft was felt with the announcement of Windows 11. The Windows that was actually supposed to be the \u0026ldquo;forever\u0026rdquo; Windows would be replaced. And this time, many PCs were not allowed to come along. With Windows 11, Microsoft made TPM 2.0 and Secure Boot mandatory. For much older hardware, this was the end. The strong encouragement to use a Microsoft account in Windows 10 now became a requirement. Of course, this could be bypassed with a few tricks, but Microsoft quickly made it clear: \u0026ldquo;This is no longer your operating system; you are just a guest here.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis mentality was only expanded further, and the pressure increased. Users were frustrated; we just wanted to use our computers. But then the unexpected happened that was to change the world: ChatGPT.\u003c/p\u003e\n\u003cp\u003eThe gates were opened, tech companies got scared, and seemingly overnight, AI was what was supposed to rule everything. From now on, everything had to have AI. AI chat, AI search, AI-AI. If it didn\u0026rsquo;t have AI, it wasn\u0026rsquo;t good. The more invasive, the more intrusive, the more unavoidable, the better. AI in Windows, AI in Paint, AI in Word, AI in Excel, AI in Notepad. Nothing was safe. AI was even supposed to play our games for us from now on.\u003c/p\u003e\n\u003ch2 id=\"a-valve-for-frustration\"\u003eA Valve for Frustration\u003c/h2\u003e\n\u003cp\u003eWhile Microsoft was working on designing our desktop more and more according to their ideas, a small company called Valve was quietly working on its own solution. With the Steam Deck, they brought a PC to the market, shaped like an overweight Nintendo Switch console. The hardware itself was very interesting, but the truly exciting part was the software. The special thing was that the device ran Linux.\u003c/p\u003e\n\u003cp\u003eIt must be said that Valve had already tried once in the past to bring a Linux console to the market. Then conceived as a living room device, the hope was that by leading by example, other developers would also release their games for Linux. However, that did not happen.\u003c/p\u003e\n\u003cp\u003eBut Valve had learned from it and invested a lot of money and time behind the scenes to make Windows games not just runnable on Linux, but run well. The Steam Deck was proof that it worked even without Microsoft and that the mantra that you can\u0026rsquo;t play games on Linux was from now on false. Of course, not everything was perfect from the start. But the foundation was laid, the infrastructure was there, and the dam was broken.\u003c/p\u003e\n\u003ch2 id=\"the-perfect-wave\"\u003eThe Perfect Wave\u003c/h2\u003e\n\u003cp\u003eSince the launch of the Steam Deck, a lot has happened in game support, and most titles run without problems (sometimes even better than under Windows). Only anti-cheat software still causes issues. Valve has announced more hardware that skips Windows. This, coupled with the increasing annoyance of Windows, has now convinced many people to give Linux a chance. And that is great news for everyone. When Microsoft suddenly has to compete with a free operating system that respects the user and constantly gets better, it can\u0026rsquo;t be bad for anyone. I very much hope for the year of the Linux desktop. It won\u0026rsquo;t always be easy, because not everyone wants to just play, and Linux still has many sharp edges.\u003c/p\u003e\n\u003cp\u003eFor me personally, however, the year of the Linux desktop already began in 2025, and I can only benefit if more people take the step with me.\u003c/p\u003e\n",
      "date_published": "2026-01-03T00:00:00+00:00",
      "date_modified": "2026-01-28T19:48:56+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2023/07/04/the-fediverse-meta-and-threads/",
      "url": "https://staging.cperrin.xyz/en/2023/07/04/the-fediverse-meta-and-threads/",
      "title": "The Fediverse, Meta, and Threads",
      "summary": "\u003cp\u003eThe Fediverse is growing. Spurred by the problems at Twitter and Reddit, more and more users are now venturing onto alternative platforms. Especially \u003ca href=\"https://joinmastodon.org\"\u003eMastodon\u003c/a\u003e, \u003ca href=\"https://join-lemmy.org\"\u003eLemmy\u003c/a\u003e, and \u003ca href=\"https://kbin.pub\"\u003eKBin\u003c/a\u003e have experienced an explosive surge in users recently. This didn\u0026rsquo;t happen without problems, of course, but it at least shows that there might be a way not to throw all data into the gullets of large corporations.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/2023/07/04/das-fediverse-meta-und-threads/fediverse.png\" alt=\"The many branches of the Fediverse\"\u003e\u003c/p\u003e\n\u003cp\u003eWith the \u003ca href=\"https://www.w3.org/TR/activitypub/\"\u003eActivityPub protocol\u003c/a\u003e as the glue between all Fediverse projects, the formation of silos should finally come to an end. Everyone can talk to everyone: Mastodon with Pixelfed, Pleroma with PeerTube, Lemmy with KBin. The boundaries between projects are blurring, even if sometimes content from one software makes no sense in the interface of another. But this is where the problem begins.\u003c/p\u003e",
      "content_html": "\u003cp\u003eThe Fediverse is growing. Spurred by the problems at Twitter and Reddit, more and more users are now venturing onto alternative platforms. Especially \u003ca href=\"https://joinmastodon.org\"\u003eMastodon\u003c/a\u003e, \u003ca href=\"https://join-lemmy.org\"\u003eLemmy\u003c/a\u003e, and \u003ca href=\"https://kbin.pub\"\u003eKBin\u003c/a\u003e have experienced an explosive surge in users recently. This didn\u0026rsquo;t happen without problems, of course, but it at least shows that there might be a way not to throw all data into the gullets of large corporations.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/2023/07/04/das-fediverse-meta-und-threads/fediverse.png\" alt=\"The many branches of the Fediverse\"\u003e\u003c/p\u003e\n\u003cp\u003eWith the \u003ca href=\"https://www.w3.org/TR/activitypub/\"\u003eActivityPub protocol\u003c/a\u003e as the glue between all Fediverse projects, the formation of silos should finally come to an end. Everyone can talk to everyone: Mastodon with Pixelfed, Pleroma with PeerTube, Lemmy with KBin. The boundaries between projects are blurring, even if sometimes content from one software makes no sense in the interface of another. But this is where the problem begins.\u003c/p\u003e\n\u003ch2 id=\"meta-threads\"\u003eMeta Threads\u003c/h2\u003e\n\u003cp\u003eBecause even at the large corporations, the mills don\u0026rsquo;t stand still, even if it might sometimes seem that way. Meta (formerly Facebook) has decided to also be part of the Fediverse revolution. With a new project now named \u003ca href=\"https://www.threads.net/\"\u003eThreads\u003c/a\u003e, they want to connect to the Fediverse. It\u0026rsquo;s supposed to be a kind of microblogging platform like Twitter or Mastodon. The announcements and leaks were, of course, received in the Fediverse with great anticipation and recognition. NOT!\u003c/p\u003e\n\u003cp\u003eMany critics were reminded of the strategy coined by Microsoft: \u0026ldquo;Embrace, Extend, Extinguish.\u0026rdquo; This involves first welcoming a project with open arms to attract users, then equipping it with proprietary extensions that are incompatible with the original, and thus slowly pushing the competitor out of the market until it is extinguished. It\u0026rsquo;s not hard to see how Meta could use such a strategy against the Fediverse.\u003c/p\u003e\n\u003cp\u003eBut the critics go further. As we all know, at the latest after the Cambridge Analytica scandal, Meta has significant problems with data protection. Additionally, their primary business model consists of creating and capitalizing on detailed user profiles. And here, the strength of the Fediverse becomes a potential weakness.\u003c/p\u003e\n\u003ch2 id=\"federation-not-just-in-star-trek\"\u003eFederation Not Just in Star Trek\u003c/h2\u003e\n\u003cp\u003eThe problem can be understood most easily with a small excursion into the subject of ActivityPub. As mentioned at the beginning, it is the protocol that enables communication between all Fediverse projects. As soon as, for example, a Mastodon instance like mastodon.social allows its users to follow users from other instances and vice versa, that server is considered federated. This applies equally to groups, communities, magazines, or other \u0026ldquo;things\u0026rdquo; you can follow. As soon as a user on Instance A follows a user on Instance B, all public updates from the user on Instance B are sent to Instance A and stored on Instance A.\u003c/p\u003e\n\u003cp\u003eThe attentive reader may have discovered the problem here. If a Threads user now follows a user on a Fediverse instance, data from Fediverse users inevitably ends up on Meta servers. This can be restricted by an instance defederating from Threads, thus blocking the instance\u0026rsquo;s communication with Threads. Many instance operators have already announced this step, while others want to wait and see. Opinions are certainly divided.\u003c/p\u003e\n\u003ch2 id=\"my-bits-alone\"\u003eMy bits alone!\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m not the biggest fan of Meta and the rest myself, but I must say that I see the panic as somewhat misdirected. I\u0026rsquo;m also not thrilled that my data flows to the large corporations, but I have also decided to publish this data. An analogy for this would be standing in a marketplace to shout your opinion to the world and then getting annoyed if someone writes it down.\u003c/p\u003e\n\u003cp\u003eThe concept hasn\u0026rsquo;t changed from Facebook, Twitter \u0026amp; Co. The data there is indeed in its own silos, but even there, data is forwarded to advertising partners and analysis firms, scraped from public pages, and processed otherwise. In the Fediverse, it\u0026rsquo;s just more obvious that the data is being forwarded. And that\u0026rsquo;s not a criticism of the Fediverse. That\u0026rsquo;s the core principle of the Fediverse.\u003c/p\u003e\n\u003ch2 id=\"social-private-media\"\u003eSocial Private Media\u003c/h2\u003e\n\u003cp\u003eWhat is the solution to the problem? My answer: There is none. At least not in the way many would like. As a society, we have forgotten that it\u0026rsquo;s not always good to stand in the marketplace and start shouting. We have started showing strangers our baby photos, sharing our location, and revealing our innermost selves. All without really making clear what the implications are. Partly they were obscured from us, partly we ignored them.\u003c/p\u003e\n\u003cp\u003eThe problems of the Fediverse, like the solution, are not new. We need to reveal less about ourselves. We must be aware that a sent Toot, just like an Instagram post, is no longer in our hands after sending. They can be processed, correlated, and made into a personal profile. For that, you don\u0026rsquo;t even need Threads from Meta. The public sharing of data is incompatible with a desire for control.\u003c/p\u003e\n\u003cp\u003eWe can try to strengthen the rights of users through laws and make the processing of data more difficult, but ultimately, even without big evil companies, one thing remains the same:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSocial Media is not Private Media\u003c/strong\u003e\u003c/p\u003e\n",
      "date_published": "2023-07-04T00:00:00+00:00",
      "date_modified": "2026-06-19T23:07:43+02:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2021/09/16/new-is-always-better/",
      "url": "https://staging.cperrin.xyz/en/2021/09/16/new-is-always-better/",
      "title": "New is Always Better (?)",
      "summary": "\u003cp\u003eI have something to confess. I have a problem. I\u0026rsquo;m not sure if my problem has a name, so I\u0026rsquo;ll give it one. I call it \u0026ldquo;Neoprogrammism.\u0026rdquo; What is it? Well, it\u0026rsquo;s best explained with an example.\u003c/p\u003e\n\u003ch2 id=\"example-1\"\u003eExample 1\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m now well-versed in Python and Django from several projects, but I\u0026rsquo;m not as familiar with JavaScript. There\u0026rsquo;s a new project to generate APIs directly from the database schema that relies entirely on JavaScript? I\u0026rsquo;ll take it!\u003c/p\u003e",
      "content_html": "\u003cp\u003eI have something to confess. I have a problem. I\u0026rsquo;m not sure if my problem has a name, so I\u0026rsquo;ll give it one. I call it \u0026ldquo;Neoprogrammism.\u0026rdquo; What is it? Well, it\u0026rsquo;s best explained with an example.\u003c/p\u003e\n\u003ch2 id=\"example-1\"\u003eExample 1\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m now well-versed in Python and Django from several projects, but I\u0026rsquo;m not as familiar with JavaScript. There\u0026rsquo;s a new project to generate APIs directly from the database schema that relies entirely on JavaScript? I\u0026rsquo;ll take it!\u003c/p\u003e\n\u003ch2 id=\"example-2\"\u003eExample 2\u003c/h2\u003e\n\u003cp\u003eJavaScript isn\u0026rsquo;t that familiar to me, but I like CSS. To make my life easier and follow best practices better, I decide on a CSS framework. Now, there are a lot of established frameworks in this area, but I recently read with half my brain in an article that there\u0026rsquo;s a new slim framework. Better take that one!\u003c/p\u003e\n\u003ch2 id=\"what-i-mean-to-say-is\"\u003eWhat I mean to say is\u0026hellip;\u003c/h2\u003e\n\u003cp\u003eNeoprogrammism can affect us all because few of us are immune to the latest \u0026ldquo;bling.\u0026rdquo; It\u0026rsquo;s often tempting to use the latest and \u0026ldquo;greatest\u0026rdquo; for a new project. That doesn\u0026rsquo;t necessarily have to be a bad thing. New projects sometimes do things better or have a chance of receiving support for longer in the future.\u003c/p\u003e\n\u003cp\u003eHowever, one must not forget that there might be a reason why \u0026ldquo;older projects\u0026rdquo; are still there and being actively developed. There might also be good reasons why these projects work the way they do. Additionally, there has been more time to build an ecosystem around them.\u003c/p\u003e\n\u003cp\u003eThe most important question I ask myself to defeat my Neoprogrammism is whether I only want to use something because it\u0026rsquo;s new or trendy.\u003c/p\u003e\n",
      "date_published": "2021-09-16T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2020/02/20/new-design-for-the-blog/",
      "url": "https://staging.cperrin.xyz/en/2020/02/20/new-design-for-the-blog/",
      "title": "New Design for the Blog",
      "summary": "\u003cp\u003eThe era of downloaded and adapted designs is over. My first own design sees the light of day. I call it cperrin-hugo-theme. Poetic, isn\u0026rsquo;t it?\u003c/p\u003e\n\u003cp\u003eBut that\u0026rsquo;s not the only new thing. I came up with my own brand-hot and ultra-creative logo to make the crowds scream.\u003c/p\u003e",
      "content_html": "\u003cp\u003eThe era of downloaded and adapted designs is over. My first own design sees the light of day. I call it cperrin-hugo-theme. Poetic, isn\u0026rsquo;t it?\u003c/p\u003e\n\u003cp\u003eBut that\u0026rsquo;s not the only new thing. I came up with my own brand-hot and ultra-creative logo to make the crowds scream.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/2020/02/20/neues-design-f%C3%BCr-den-blog/logo.png\" alt=\"New Logo\"\u003e\u003c/p\u003e\n\u003cp\u003eDoes the work make sense, considering how many posts I\u0026rsquo;ve written since the beginning of the blog? Certainly not! Was it fun? Yeah. Was it a good way not to do what I should be doing? Definitely!!\u003c/p\u003e",
      "date_published": "2020-02-20T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2017/09/08/ansible-devops-made-easy/",
      "url": "https://staging.cperrin.xyz/en/2017/09/08/ansible-devops-made-easy/",
      "title": "Ansible: DevOps Made Easy",
      "summary": "\u003cp\u003eThe problem is quite old: A large number of servers need to be set up or homogenized, ideally with minimal effort. This problem has been solved several times. So-called orchestration tools execute specific commands on multiple systems based on predefined instructions.\u003c/p\u003e\n\u003cp\u003eExamples of such tools are \u003ca href=\"https://puppet.com/\"\u003ePuppet\u003c/a\u003e, \u003ca href=\"https://www.chef.io/\"\u003eChef\u003c/a\u003e, or \u003ca href=\"https://cfengine.com/\"\u003eCFEngine\u003c/a\u003e. However, these representatives have one major disadvantage for me: they require a client to be installed on the remote side for execution.\u003c/p\u003e\n\u003cp\u003eThis is where \u003ca href=\"https://www.ansible.com/\"\u003eAnsible\u003c/a\u003e comes in. Ansible uses only an SSH connection to execute scripts or playbooks on the clients.\u003c/p\u003e",
      "content_html": "\u003cp\u003eThe problem is quite old: A large number of servers need to be set up or homogenized, ideally with minimal effort. This problem has been solved several times. So-called orchestration tools execute specific commands on multiple systems based on predefined instructions.\u003c/p\u003e\n\u003cp\u003eExamples of such tools are \u003ca href=\"https://puppet.com/\"\u003ePuppet\u003c/a\u003e, \u003ca href=\"https://www.chef.io/\"\u003eChef\u003c/a\u003e, or \u003ca href=\"https://cfengine.com/\"\u003eCFEngine\u003c/a\u003e. However, these representatives have one major disadvantage for me: they require a client to be installed on the remote side for execution.\u003c/p\u003e\n\u003cp\u003eThis is where \u003ca href=\"https://www.ansible.com/\"\u003eAnsible\u003c/a\u003e comes in. Ansible uses only an SSH connection to execute scripts or playbooks on the clients.\u003c/p\u003e\n\u003cp\u003eThe best way to demonstrate the simplicity of Ansible is with a small example in which a list of packages is installed via apt. First, we create the basic structure:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir ansible\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ansible\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext, we need to create an inventory, which is a list of servers and groups on which the playbooks will be executed.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ansible/inventory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[group1]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eserver1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eserver2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eserver3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this example, we have a group called \u003ccode\u003egroup1\u003c/code\u003e containing servers 1-3.\u003c/p\u003e\n\u003cp\u003eNext, we need to create a playbook containing the instructions for Ansible:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ansible/site.yml\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nn\"\u003e---\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003ehosts\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eall                \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# select servers\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ebecome\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003eyes\u003c/span\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"c\"\u003e# execute as sudo\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003etasks\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e                     \u003c/span\u003e\u003cspan class=\"c\"\u003e# list of tasks\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eInstall packages\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# name for the task\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eapt\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e                   \u003c/span\u003e\u003cspan class=\"c\"\u003e# use the APT module\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ items }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# name of the package\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epresent      \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# status of the package\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith_items\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c\"\u003e# loop with a list. fills {{ items }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003eprogram1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003eprogram2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003eprogram3\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis playbook installs the programs \u003ccode\u003eprogram1-3\u003c/code\u003e via apt.\u003c/p\u003e\n\u003cp\u003eTo run this playbook, we just need to execute:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eansible-playbook site.yml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you haven\u0026rsquo;t set up SSH key authentication, you need to append the \u003ccode\u003e-k\u003c/code\u003e option. To authenticate with a user other than the currently logged-in user, use the \u003ccode\u003e-u \u0026lt;username\u0026gt;\u003c/code\u003e option.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s it. Ansible should now connect to all servers via SSH and install the listed packages. If the packages are already installed, Ansible will simply skip them.\u003c/p\u003e\n\u003cp\u003eAnsible now has a \u003ca href=\"https://docs.ansible.com/ansible/latest/collections/index.html\"\u003ehuge number of modules\u003c/a\u003e to choose from, allowing you to use Ansible in a wide variety of use cases.\u003c/p\u003e",
      "date_published": "2017-09-08T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2017/01/02/seafile-owncloud-only-better/",
      "url": "https://staging.cperrin.xyz/en/2017/01/02/seafile-owncloud-only-better/",
      "title": "Seafile: OwnCloud, Only Better",
      "summary": "\u003cp\u003eAnyone who has read my \u003ca href=\"https://staging.cperrin.xyz/en/2012/08/13/owncloud-self-hosted-dropbox/\"\u003epost about OwnCloud\u003c/a\u003e will know that I strive to store my data, if possible, not with Dropbox \u0026amp; Co. In the past, OwnCloud was the best and simplest solution, but for some time now, there has been an alternative that is worth seeing: \u003ca href=\"https://seafile.com\"\u003eSeafile\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSeafile offers free sync clients for every common platform (Windows, Linux, macOS, Android, iOS).\u003c/p\u003e\n\u003cp\u003eIn the background, the application, written in Python, works in two parts. The \u003ccode\u003eseafile\u003c/code\u003e application controls the saving and syncing of data. It uses a filesystem based on git, which means that by default, all files are versioned. The other part is \u003ccode\u003eseahub\u003c/code\u003e, which provides the web interface to Seafile.\u003c/p\u003e",
      "content_html": "\u003cp\u003eAnyone who has read my \u003ca href=\"https://staging.cperrin.xyz/en/2012/08/13/owncloud-self-hosted-dropbox/\"\u003epost about OwnCloud\u003c/a\u003e will know that I strive to store my data, if possible, not with Dropbox \u0026amp; Co. In the past, OwnCloud was the best and simplest solution, but for some time now, there has been an alternative that is worth seeing: \u003ca href=\"https://seafile.com\"\u003eSeafile\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSeafile offers free sync clients for every common platform (Windows, Linux, macOS, Android, iOS).\u003c/p\u003e\n\u003cp\u003eIn the background, the application, written in Python, works in two parts. The \u003ccode\u003eseafile\u003c/code\u003e application controls the saving and syncing of data. It uses a filesystem based on git, which means that by default, all files are versioned. The other part is \u003ccode\u003eseahub\u003c/code\u003e, which provides the web interface to Seafile.\u003c/p\u003e\n\u003cp\u003eFrom my perspective, Seafile has the major advantage that it is not written in PHP. One has to torture PHP very much to extract functionality like OwnCloud from it. This is noticeable in performance. Likewise, the separation of the web interface and the sync server is advantageous. Sync clients speak directly to the sync server, and the website is just another sync client.\u003c/p\u003e\n",
      "date_published": "2017-01-02T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2016/05/19/moving-to-hugo-and-a-new-domain/",
      "url": "https://staging.cperrin.xyz/en/2016/05/19/moving-to-hugo-and-a-new-domain/",
      "title": "Moving to Hugo and a New Domain",
      "summary": "\u003cp\u003eI have long maintained a WordPress blog and rarely posted anything. In the process, I repeatedly noticed how slow WordPress actually is. Additionally, I need PHP and a database to keep everything running. This, coupled with the fact that there were recurring security vulnerabilities in WordPress, led me to search for an alternative.\u003c/p\u003e\n\u003cp\u003eSince I don\u0026rsquo;t need much dynamic content on my website, I looked for a static site generator and came across \u003ca href=\"https://gohugo.io\"\u003eHugo\u003c/a\u003e.\u003c/p\u003e",
      "content_html": "\u003cp\u003eI have long maintained a WordPress blog and rarely posted anything. In the process, I repeatedly noticed how slow WordPress actually is. Additionally, I need PHP and a database to keep everything running. This, coupled with the fact that there were recurring security vulnerabilities in WordPress, led me to search for an alternative.\u003c/p\u003e\n\u003cp\u003eSince I don\u0026rsquo;t need much dynamic content on my website, I looked for a static site generator and came across \u003ca href=\"https://gohugo.io\"\u003eHugo\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHugo is an application written in Go that generates static HTML files from Markdown files and templates. I now deliver these only with an Nginx and have a beautiful blog without PHP and MySQL. Thanks to Disqus, I don\u0026rsquo;t even have to give up a comment function.\u003c/p\u003e\n\u003cp\u003eTo make my life even easier, I use \u003ca href=\"https://gogs.io\"\u003eGogs\u003c/a\u003e and \u003ca href=\"https://drone.io/\"\u003eDrone.io\u003c/a\u003e to automatically build my blog and copy it to the server. For this, I use the following \u003ccode\u003e.drone.yml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eproduction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epublysher/hugo\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommands\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003ehugo --destination=\u0026lt;production_source\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ewhen\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003emaster\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003estaging\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epublysher/hugo\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommands\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003ehugo --destination=\u0026lt;staging_source\u0026gt; --buildDrafts=true --buildFuture=true --baseUrl=\u0026lt;staging_url\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003epublish\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ersync\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ehost\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;server\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;user\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;staging_source\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;staging_target\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003erecursive\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ersync\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ehost\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;server\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;user\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;production_source\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e\u0026lt;production_target\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003erecursive\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ewhen\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003emaster\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis ensures that every push to my Gogs repo triggers a build and loads a production and a staging version onto the server. It\u0026rsquo;s almost magic.\u003c/p\u003e\n\u003cp\u003eTo top it all off, I have acquired a new and more personal domain. My blog will be found at \u003ca href=\"https://cperrin.xyz\"\u003ehttps://cperrin.xyz\u003c/a\u003e in the future.\u003c/p\u003e",
      "date_published": "2016-05-19T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2016/05/13/pammysqltools-manage-libnss-mysql-and-pam-mysql-users-via-the-shell/",
      "url": "https://staging.cperrin.xyz/en/2016/05/13/pammysqltools-manage-libnss-mysql-and-pam-mysql-users-via-the-shell/",
      "title": "PAMMySQLTools: Manage libnss-mysql and pam-mysql Users via the Shell",
      "summary": "\u003cp\u003eThrough my work, I faced the problem that we wanted to authenticate users centrally via MySQL. This is not inherently a problem thanks to \u003cem\u003elibnss-mysql\u003c/em\u003e and \u003cem\u003epam-mysql\u003c/em\u003e, but unfortunately, there is a lack of programs to manage users effectively.\u003c/p\u003e\n\u003cp\u003eTherefore, I have written tools that imitate the Linux programs \u003ccode\u003euser{add,mod,del}\u003c/code\u003e and \u003ccode\u003egroup{add,mod,del}\u003c/code\u003e and published them as \u003ca href=\"https://github.com/cperrin88/PAMMySQLTools\"\u003ePAMMySQLTools\u003c/a\u003e on \u003ca href=\"https://pypi.python.org/pypi/PAMMySQLTools\"\u003ePyPI\u003c/a\u003e.\u003c/p\u003e",
      "content_html": "\u003cp\u003eThrough my work, I faced the problem that we wanted to authenticate users centrally via MySQL. This is not inherently a problem thanks to \u003cem\u003elibnss-mysql\u003c/em\u003e and \u003cem\u003epam-mysql\u003c/em\u003e, but unfortunately, there is a lack of programs to manage users effectively.\u003c/p\u003e\n\u003cp\u003eTherefore, I have written tools that imitate the Linux programs \u003ccode\u003euser{add,mod,del}\u003c/code\u003e and \u003ccode\u003egroup{add,mod,del}\u003c/code\u003e and published them as \u003ca href=\"https://github.com/cperrin88/PAMMySQLTools\"\u003ePAMMySQLTools\u003c/a\u003e on \u003ca href=\"https://pypi.python.org/pypi/PAMMySQLTools\"\u003ePyPI\u003c/a\u003e.\u003c/p\u003e\n\u003ch1 id=\"installation\"\u003eInstallation\u003c/h1\u003e\n\u003cp\u003eThanks to \u003ccode\u003epip\u003c/code\u003e, you only need to run the command:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epip install pammysqltools\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eor\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epip3 install pammysqltools\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eto install the tools. This will install the following programs:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003emyuseradd\u003c/li\u003e\n\u003cli\u003emyusermod\u003c/li\u003e\n\u003cli\u003emyuserdel\u003c/li\u003e\n\u003cli\u003emygroupadd\u003c/li\u003e\n\u003cli\u003emygroupmod\u003c/li\u003e\n\u003cli\u003emygroupdel\u003c/li\u003e\n\u003cli\u003emyimportusers\u003c/li\u003e\n\u003cli\u003emyimportgroups\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe \u003ccode\u003emyuser*\u003c/code\u003e and \u003ccode\u003emygroup*\u003c/code\u003e tools are mostly modeled after the Linux programs and behave largely the same.\u003c/p\u003e\n\u003cp\u003eThe tools \u003ccode\u003emyimportusers\u003c/code\u003e and \u003ccode\u003emyimportgroups\u003c/code\u003e import users from \u003ccode\u003e/etc/passwd\u003c/code\u003e and \u003ccode\u003e/etc/groups\u003c/code\u003e, or \u003ccode\u003e/etc/shadow\u003c/code\u003e and \u003ccode\u003e/etc/gshadow\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFor an overview of the arguments, simply append \u003ccode\u003e--help\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eExample: myuseradd\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ myuseradd --help\nUsage: myuseradd [OPTIONS] LOGIN\n\nOptions:\n  -b, --basedir BASE_DIR          base directory for the home directory of the\n                                  new account\n  -c, --comment COMMENT           GECOS field of the new account\n  -d, --home-dir HOME_DIR         home directory of the new account\n  -e, --expiredate EXPIRE_DATE    expiration date of the new account\n  -f, --inactive INACTIVE         password inactivity period of the new\n                                  account\n  -g, --gid GROUP                 name or ID of the primary group of the new\n                                  account\n  -G, --groups GROUPS             list of supplementary groups of the new\n                                  account\n  -k, --skel SKEL_DIR             use this alternative skeleton directory\n  -K, --key KEY=VALUE             override /etc/login.defs defaults\n  -M, --no-create-home / -m, --create-home\n                                  do not create the user\u0026#39;s home directory\n  -U, --no-user-group / -N, --user-group\n                                  do not create a group with the same name as\n                                  the user\n  -o, --non-unique                allow to create users with duplicate (non-\n                                  unique) UID\n  -p, --password PASSWORD         encrypted password of the new account\n  -r, --system                    create a system account\n  -s, --shell SHELL               login shell of the new account\n  -u, --uid UID                   user ID of the new account\n  --config CONF_PATH              path to the config file for this tool\n  --help                          Show this message and exit.\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"configuration\"\u003eConfiguration\u003c/h1\u003e\n\u003cp\u003eTo customize the database connection and the naming of tables and columns, the file \u003ccode\u003e/etc/pam_mysql_manager.conf\u003c/code\u003e must be created according to \u003ca href=\"https://github.com/cperrin88/PAMMySQLTools/blob/master/conf/pam_mysql_manager.conf\"\u003ethis scheme\u003c/a\u003e. Alternatively, the path to the configuration file can be passed with the \u003ccode\u003e--config\u003c/code\u003e argument. For all options not specified here, the values in the sample file are the defaults.\u003c/p\u003e\n\u003cp\u003eOnce everything is set up, you\u0026rsquo;re ready to go.\u003c/p\u003e\n\u003ch1 id=\"examples\"\u003eExamples\u003c/h1\u003e\n\u003cp\u003eCreate a user:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ myuseradd testuser\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eChange the shell:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ myusermod -s /bin/zsh testuser\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eImport groups without passwords:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ mygroupimport -i\n\u003c/code\u003e\u003c/pre\u003e",
      "date_published": "2016-05-13T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2012/12/21/carp-haproxy-high-availability-for-beginners/",
      "url": "https://staging.cperrin.xyz/en/2012/12/21/carp-haproxy-high-availability-for-beginners/",
      "title": "CARP \u0026 HAProxy: High-Availability for Beginners",
      "summary": "\u003cp\u003eThe field of high-availability in the web area is often complicated, large, and hard to grasp. But it doesn\u0026rsquo;t have to be, thanks to CARP and HAProxy. The combination of these two techniques makes it possible to distribute requests to multiple servers and thereby avoid the single-point-of-failure problem. For this, we only need at least 2 computers with FreeBSD (or another BSD system with CARP support) that are in the same subnet and some time to set up the following software:\u003c/p\u003e",
      "content_html": "\u003cp\u003eThe field of high-availability in the web area is often complicated, large, and hard to grasp. But it doesn\u0026rsquo;t have to be, thanks to CARP and HAProxy. The combination of these two techniques makes it possible to distribute requests to multiple servers and thereby avoid the single-point-of-failure problem. For this, we only need at least 2 computers with FreeBSD (or another BSD system with CARP support) that are in the same subnet and some time to set up the following software:\u003c/p\u003e\n\u003ch1 id=\"haproxy\"\u003eHAProxy\u003c/h1\u003e\n\u003cp\u003eHAProxy is a reverse TCP proxy with load-balancing functionality. It distributes the load to multiple (backend) servers and detects when one of them is no longer reachable. HAProxy can be used for balancing both MySQL requests and HTTP requests. I will focus exclusively on the latter here.\u003c/p\u003e\n\u003ch2 id=\"installation\"\u003eInstallation\u003c/h2\u003e\n\u003cp\u003eThe easiest way to install it is through the ports tree:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd /usr/ports/net/haproxy\nmake install clean\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis compiles and installs HAProxy on the system.\u003c/p\u003e\n\u003ch2 id=\"configuration\"\u003eConfiguration\u003c/h2\u003e\n\u003cp\u003eThe configuration file for HAProxy is located at \u003ccode\u003e/usr/local/etc/haproxy.conf\u003c/code\u003e. A starting point for a working configuration could be the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-conf\" data-lang=\"conf\"\u003eglobal\n        daemon\n        maxconn 256\n\ndefaults\n        mode http\n        timeout connect 5000ms\n        timeout client 5000ms\n        timeout server 5000ms\n        stats enable\n\nfrontend http-in\n        option httplog\n        log global\n        default_backend servers\n\nbackend servers\n        server server1 server1.example.com:8080 check\n        server server2 server2.example.com:8080 check\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"carp\"\u003eCARP\u003c/h1\u003e\n\u003cp\u003eCARP stands for \u0026ldquo;Common Address Redundancy Protocol\u0026rdquo;. It allows an IP address to be automatically taken over by another host if it is no longer reachable. The prerequisite for this is that all computers are in the same network segment, as CARP works via broadcast.\u003c/p\u003e\n\u003ch2 id=\"installation-1\"\u003eInstallation\u003c/h2\u003e\n\u003cp\u003eStarting with FreeBSD 9.0, CARP is enabled by default. For older versions, support must either be compiled into the kernel or the kernel module must be activated with the entry:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-conf\" data-lang=\"conf\"\u003eif_carp_load=\u0026#34;YES\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ein the file \u003ccode\u003e/boot/loader.conf\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"configuration-files\"\u003eConfiguration Files\u003c/h2\u003e\n\u003cp\u003eThe configuration of CARP is relatively simple. First, a master server is configured. This server holds the IP by default.\u003c/p\u003e\n\u003cp\u003eTo do this, we need to create and configure a CARP interface. During operation, we use:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eifconfig carp0 create\nifconfig vhid 1 pass \u0026lt;password\u0026gt; 10.10.0.1/16\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe password and the vhid number must be the same on all backup systems. To ensure this configuration works after a restart, we add the following lines to \u003ccode\u003e/etc/rc.conf\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-conf\" data-lang=\"conf\"\u003ecloned_interfaces=\u0026#34;carp0\u0026#34;\nifconfig_carp0=\u0026#34;vhid 1 pass \u0026lt;password\u0026gt; 10.10.0.1/16\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOn the backup systems, the CARP interface is created in a similar way, but the \u0026ldquo;advskew\u0026rdquo; argument is inserted:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-conf\" data-lang=\"conf\"\u003ecloned_interfaces=\u0026#34;carp0\u0026#34;\nifconfig_carp0=\u0026#34;vhid 1 advskew 100 pass \u0026lt;password\u0026gt; 10.10.0.1/16\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe higher the value after \u0026ldquo;advskew\u0026rdquo;, the lower the priority if the main system fails.\u003c/p\u003e",
      "date_published": "2012-12-21T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    },
    {
      "id": "https://staging.cperrin.xyz/en/2012/08/13/owncloud-self-hosted-dropbox/",
      "url": "https://staging.cperrin.xyz/en/2012/08/13/owncloud-self-hosted-dropbox/",
      "title": "OwnCloud: Self-Hosted Dropbox",
      "summary": "\u003cp\u003eDropbox is one of the most practical tools that help me in my daily work. Whether it\u0026rsquo;s just synchronizing data or even sharing it with others, it\u0026rsquo;s possible with just a few clicks.\u003c/p\u003e\n\u003cp\u003eUnfortunately, the whole thing has a serious drawback: My data is no longer with me! Although data in Dropbox is reportedly encrypted, you can NEVER be sure what might happen to your own data.\u003c/p\u003e\n\u003cp\u003eFor this dilemma, there is now a solution: \u003ca href=\"http://owncloud.org/\"\u003eOwnCloud\u003c/a\u003e\u003c/p\u003e",
      "content_html": "\u003cp\u003eDropbox is one of the most practical tools that help me in my daily work. Whether it\u0026rsquo;s just synchronizing data or even sharing it with others, it\u0026rsquo;s possible with just a few clicks.\u003c/p\u003e\n\u003cp\u003eUnfortunately, the whole thing has a serious drawback: My data is no longer with me! Although data in Dropbox is reportedly encrypted, you can NEVER be sure what might happen to your own data.\u003c/p\u003e\n\u003cp\u003eFor this dilemma, there is now a solution: \u003ca href=\"http://owncloud.org/\"\u003eOwnCloud\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOwnCloud is an Open Source PHP application to offer your own Dropbox-like service. With OwnCloud, it is easy to maintain full control over your data without much extra effort.\u003c/p\u003e\n\u003cp\u003eThe installation is child\u0026rsquo;s play and doesn\u0026rsquo;t even necessarily require the installation of a MySQL server. By default, SQLite is used. The installation can be divided into two simple steps:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eWebserver Configuration: There are templates for many servers on the OwnCloud website.\u003c/li\u003e\n\u003cli\u003eOwnCloud Setup: The setup of OwnCloud consists of ONE page. You only need to create an admin account and, if desired, specify a MySQL server.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003eFinished!\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBy now, there is also a sync client for almost every platform (Linux, Mac, Windows, Android, iOS).\u003c/p\u003e\n\u003cp\u003eHowever, OwnCloud wants to be more than just a simple Dropbox replacement. Here are some features:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCalendar\u003c/li\u003e\n\u003cli\u003eMusic player for stored music\u003c/li\u003e\n\u003cli\u003eContacts\u003c/li\u003e\n\u003cli\u003eImage gallery\u003c/li\u003e\n\u003cli\u003eEncryption of files on the server\u003c/li\u003e\n\u003cli\u003eIntegration of Dropbox, GDrive, etc., into your own OwnCloud\u003c/li\u003e\n\u003cli\u003eAmpache/WebDAV/CalDAV/CardDAV server\u003c/li\u003e\n\u003cli\u003ePlugin interface\u003c/li\u003e\n\u003cli\u003eLDAP authentication\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOwnCloud is definitely worth a look and has by now become a truly viable alternative to Dropbox and similar services.\u003c/p\u003e",
      "date_published": "2012-08-13T00:00:00+00:00",
      "date_modified": "2026-01-28T14:29:44+01:00"
    }
  ]
}
