[{"content":"TL;DR Add a workflow at .github/workflows/accessibility.yml\nAdd lighthouserc.json with your URLs and assertions\nCheckout, build your project (Hugo, Symfony, …), then run Lighthouse CI — via npx --yes @lhci/cli@0.15.1 autorun or the treosh/lighthouse-ci-action\nRun the same checks locally before push via npx\nInfo Keep in mind that URLs used in the examples are specific for my testing applications. Your applications might have other routes you want to test with lightouse.\nExample for Hugo Projects Github Workflow name: Accessibility on: push: branches: [main] jobs: lighthouse: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 with: persist-credentials: false - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c #v6.4.0 with: go-version-file: go.mod cache: true - uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 #v3.2.1 with: extended: true - name: Build site run: hugo build --minify --gc - name: Lighthouse accessibility uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 #v12.6.2 with: configPath: ./lighthouserc.json lighthouserc.json { \u0026#34;ci\u0026#34;: { \u0026#34;collect\u0026#34;: { \u0026#34;staticDistDir\u0026#34;: \u0026#34;./public\u0026#34;, \u0026#34;url\u0026#34;: [ \u0026#34;http://localhost/\u0026#34;, \u0026#34;http://localhost/about/\u0026#34;, \u0026#34;http://localhost/nginx-config-tests/\u0026#34; ], \u0026#34;numberOfRuns\u0026#34;: 1, \u0026#34;settings\u0026#34;: { \u0026#34;onlyCategories\u0026#34;: [\u0026#34;accessibility\u0026#34;] } }, \u0026#34;assert\u0026#34;: { \u0026#34;assertions\u0026#34;: { \u0026#34;categories:accessibility\u0026#34;: [\u0026#34;error\u0026#34;, { \u0026#34;minScore\u0026#34;: 1 }], \u0026#34;color-contrast\u0026#34;: \u0026#34;error\u0026#34; } } } } How it works staticDistDir starts a file server that is used by lighthouse. Because the build artifacts of Hugo are in public, we point to that direction. I chose the url array so that I test articles and pages that use all of the different features of my theme like quotes, images, codeblocks. In consequence, all kinds of content display are covered regarding the accessibility check. onlyCategories \u0026ldquo;accessibility\u0026rdquo; does, what I want - check the accessibility criteria with lighthouse. You can also use the other categories of lighthouse (seo, performance, best-practices) assert: what I want to assert Run locally with npx Same command as in CI — build first, then invoke Lighthouse CI with a pinned version:\nhugo build --minify --gc npx --yes @lhci/cli@0.15.1 autorun Example for Symfony Projects Info Please notice that I use a dockerized setup with justfile to keep things convenient. Your setup might differ, so you might need to adjust the workflow regarding commands. For example just compose pull is docker compose pull. If you run without docker, you do not need that and just have to make sure you start up your Symfony app, so the routes configured for lighthouse are accessible during the CI run, so lighthouse tests can be performed.\nGithub Workflow name: Accessibility on: push: branches: [main] jobs: lighthouse: runs-on: ubuntu-latest steps: - name: Install Just uses: extractions/setup-just@v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 with: persist-credentials: false - name: Pulling Docker images run: just compose pull - name: Building Docker images run: just compose build - name: Install dependencies run: just install - name: Start application run: just init-a11y - name: Lighthouse accessibility uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 #v12.6.2 with: configPath: ./lighthouserc.json lighthouserc.json { \u0026#34;ci\u0026#34;: { \u0026#34;collect\u0026#34;: { \u0026#34;url\u0026#34;: [ \u0026#34;http://localhost:8080/\u0026#34; ], \u0026#34;numberOfRuns\u0026#34;: 1, \u0026#34;settings\u0026#34;: { \u0026#34;onlyCategories\u0026#34;: [\u0026#34;accessibility\u0026#34;] } }, \u0026#34;assert\u0026#34;: { \u0026#34;assertions\u0026#34;: { \u0026#34;categories:accessibility\u0026#34;: [\u0026#34;error\u0026#34;, { \u0026#34;minScore\u0026#34;: 1 }], \u0026#34;color-contrast\u0026#34;: \u0026#34;error\u0026#34; } } } } My Symfony app runs at localhost:8080 (nginx-unpriviledged). Thus, this url is used in the configuration.\nExecute lighthouse locally via justfile recipe Tip If you are not familiar with just, check out my related article on just or the official documentation. Regarding the commands below, the variables defined at the start can be substituted in the recipes 1:1.\nFor example {{PHP-DB-RUN}} === docker compose -f docker/compose.yml run --rm php-fpm.\nCOMPOSE := \u0026#39;docker compose -f docker/compose.yml\u0026#39; COMPOSE-RUN := COMPOSE + \u0026#39; run --rm\u0026#39; PHP-DB-RUN := COMPOSE-RUN + \u0026#39; php-fpm\u0026#39; [private] init-a11y *args: APP_ENV=prod {{COMPOSE}} up -d {{args}} {{DB-RUN}} mysqladmin -uroot -pChangeMe -hdb --wait=10 ping APP_ENV=prod {{PHP-DB-RUN}} bin/console cache:warmup --env=prod APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:database:drop --force --if-exists --env=prod APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:database:create --env=prod APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:schema:create --env=prod APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:migrations:sync-metadata-storage --no-interaction --env=prod APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:migrations:version --add --all --no-interaction --env=prod # run lighthouse accessibility checks against the home page a11y: #!/usr/bin/env bash set -euo pipefail if ! curl -sf http://localhost:8080/ \u0026gt; /dev/null || curl -s http://localhost:8080/drives | grep -q \u0026#39;sf-toolbar\u0026#39;; then echo \u0026#34;Starting application in prod mode for accessibility audit...\u0026#34; just init-a11y fi npx --yes @lhci/cli@0.15.1 autorun Differences between Hugo and Symfony The Lighthouse configuration is similar; the surrounding setup is not.\nHugo is static. You build HTML into public/ and point Lighthouse at that folder via staticDistDir. Lighthouse starts its own file server — no app process, no database, no Docker. CI is essentially: checkout → build Hugo → run Lighthouse.\nSymfony is dynamic. Pages are rendered at request time, so Lighthouse needs a running application. Your lighthouserc.json lists live URLs (for example http://localhost:8080/) and CI must boot the stack first — containers, dependencies, database schema, cache warmup. That is heavier, but it audits what users actually get, including server-rendered markup and runtime-served assets.\nRunning Lighthouse: GitHub Action vs npx Two approaches appear in this article; both call the same @lhci/cli under the hood.\nGitHub Action (treosh/lighthouse-ci-action) npx (npx --yes @lhci/cli@0.14.0 autorun) Used here CI local runs Pros Chrome and Lighthouse preconfigured; uploadArtifacts stores HTML reports on failure; no install step in the workflow YAML No package.json or lockfile for Lighthouse; version pinned in the command Cons Extra abstraction; local runs need npx (or npm) to mirror CI Downloads the CLI on cold runs; you must keep the @version suffix in sync everywhere you use it Practical takeaway: There are three ways to use lighthouse-ci. You can run it directly via npx (which you could also do in CI). Secondly, you can use the GitHub action for CI (community developed) and rely on npx locally. As third option, you can also require @lhci/cli as dev dependency and add an npm script to run lighthouse-ci (see below).\nAlternative: install via npm Instead of npx, add @lhci/cli as a devDependency and a script in package.json, then all you need is to run npm run a11y to execute lighthouse:\n\u0026#34;scripts\u0026#34;: { \u0026#34;a11y\u0026#34;: \u0026#34;hugo build --minify --gc \u0026amp;\u0026amp; lhci autorun\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;@lhci/cli\u0026#34;: \u0026#34;0.15.1\u0026#34; } The benefit of this approach is version control via the lockfile and faster repeat runs when cached. But you pay for it with pulling in the Node toolchain, even if the site itself is not a Node app. I would recommend the approach via npm, if you already maintain a package.json for other tooling.\nAdvanced Usage Apart from accessibility checks, the lighthouse package also provides the other lighthouse checks (seo, best-practices, performance).\nYou can use uploadArtifacts in your GitHub workflow to see a nice html report of the lighthouse checks.\nApart from checks on code, there also is a community project that offers a Lighthouse CI Compare Action, which compares the current commit against the ancestor commit and creates a nice reporting.\nSources \u0026amp; Further Reading lighthouse-ci GitHub repo: https://github.com/GoogleChrome/lighthouse-ci Getting started quide: https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/getting-started.md npm package: https://www.npmjs.com/package/@lhci/cli GitHub action: https://github.com/treosh/lighthouse-ci-action Github compare action: https://github.com/adevinta/actions-lighthouseci-compare GitHub actions general documentation: https://docs.github.com/en/actions ","date":"2026-05-31","permalink":"/automate-accessibility-checks/","summary":"TL;DR Add a workflow at .github/workflows/accessibility.yml\nAdd lighthouserc.json with your URLs and assertions\nCheckout, build your project (Hugo, Symfony, …), then run Lighthouse CI — via npx --yes @lhci/cli@0.15.1 autorun or the treosh/lighthouse-ci-action\nRun the same checks locally before push via npx\nInfo ","title":"Automate Accessibility Checks with GitHub Workflows"},{"content":"TL;DR Put your nginx config (mostly contents of /etc/nginx) under version control (private repository) Use the official Gixy docker image to test your configuration with Gixy Use docker to verify your nginx config is working with nginx -t. You will need to craft a Dockerfile that can be different depending on your needs Optional: Simplify your local workflow with a justfile and handy recipes like test-nginx-gixy or test-nginx-t What is Gixy? And what does it test? You can think of Gixy as a static nginx config analyzer. As stated on the official website, Gixy reviews nginx configuration for security risks, misconfigurations, and missed hardening opportunities.\nFor all issues, Gixy can detect, getpagespeed.com also offers a comprehensive documentation. When I started using Gixy, it for example found, that I had a weak SSL/TLS configuration. After running Gixy in the terminal, you get a report printed to the CLI. Each issue also has a link to the documentation. For my example, that would be: https://gixy.getpagespeed.com/checks/weak-ssl-tls/. Apart from an explanation what was detected, you will mostly also find examples on good and bad configurations.\nProject setup I chose a \u0026ldquo;keep it simple\u0026rdquo; approach and versioned my /etc/nginx directory content. I then can easily pass all the nginx config files to the Gixy docker container via a volume mount.\nTip Depending on your server setup, you might need to add some more mounts.\nIf files are missing, because they are included in your nginx config but not located at /etc/nginx, Gixy will point that out to you in the report as well. It is important that you add those files, because they might have config that is important but would be missing in the Gixy analysis (although applied in production).\nUnderstand Gixy CLI output After you execute docker run --rm -v /path/to/your/etc/nginx:/etc/nginx:ro getpagespeed/gixy /etc/nginx/nginx.conf, Gixy will print a report to your CLI.\nWhat you see first is the summary at the end:\n==================== Summary =================== Total issues: Unspecified: 0 Low: 1 Medium: 5 High: 3 Issues look like this:\n------------------------------------------------ \u0026gt;\u0026gt; Problem: [weak_ssl_tls] Server cipher preference enabled unnecessarily Severity: LOW Description: Using outdated TLS protocols (TLSv1.0, TLSv1.1) or weak cipher suites exposes your server to attacks such as POODLE, BEAST, and SWEET32. Modern configurations should use TLSv1.2+ with strong AEAD ciphers. Additional info: https://gixy.getpagespeed.com/checks/weak-ssl-tls/ Reason: ssl_prefer_server_ciphers is on, forcing server cipher order. With modern cipher lists (all strong AEAD ciphers), client cipher preference improves performance — mobile clients without AES-NI benefit from choosing ChaCha20-Poly1305 over AES-GCM. Mozilla and nginx maintainers recommend off. Pseudo config: ssl_prefer_server_ciphers on; I think the most valuable information is the \u0026ldquo;Additional info\u0026rdquo; and the \u0026ldquo;Pseudo config\u0026rdquo; section. While the first one aids you with a link to the Gixy documentation where you will find details about the reason for the issue pointed out as well as examples on how to fix it, the \u0026ldquo;Pseudo config\u0026rdquo; section helps you pinpoint the location of the issue and apply the fix.\nDockerfile for testing nginx config with nginx -t Gixy is a static analyzer. It cannot tell you, if your nginx config is valid and thus a restart of nginx after applying configuration changes is safe. Nginx has a built in command to check the config for such issues, nginx -t. To be able to run it locally, when testing your config, you need to create a custom Dockerfile and add SSL certificates. In order to be close to production, you can check your nginx version on your server and use the official nginx docker image with the according version.\nWarning This exemplary Dockerfile might aid you in your setup, but you can most likely not just copy/paste it. Depending on your nginx version, used ciphers and SSL certificate provider, it might differ.\nFROM nginx:your.production.version # required if you use DHE ciphers RUN mkdir -p /etc/letsencrypt \u0026amp;\u0026amp; openssl dhparam -out /etc/letsencrypt/ssl-dhparams.pem 2048 # generate certificates and place them at the right location; specific for letsencrypt RUN for d in your-domain-config-under-test.com; do \\ mkdir -p /etc/letsencrypt/live/$d; \\ openssl req -new -newkey rsa:2048 -days 7 -nodes -x509 \\ -subj \u0026#34;/CN=$d\u0026#34; \\ -keyout /etc/letsencrypt/live/$d/privkey.pem -out /etc/letsencrypt/live/$d/fullchain.pem; \\ done Justfile for convenience [private] @default: just --list # build custom nginx image build: docker build . -t nginx-test # validate nginx config with nginx -t test-nginx-t: build docker run --rm -v {{justfile_directory()}}:/etc/nginx:ro -v {{justfile_directory()}}/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf:ro nginx-test nginx -t # test nginx config with Gixy test-nginx-gixy: docker run --rm -v {{justfile_directory()}}:/etc/nginx:ro -v {{justfile_directory()}}/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf:ro getpagespeed/gixy /etc/nginx/nginx.conf # preview potential Gixy auto-fixes fix-nginx-dry-run: docker run --rm -v {{justfile_directory()}}:/etc/nginx:ro getpagespeed/gixy --fix-dry-run /etc/nginx/nginx.conf # apply Gixy autofixes fix-nginx: docker run --rm -v {{justfile_directory()}}:/etc/nginx:ro getpagespeed/gixy --fix /etc/nginx/nginx.conf ","date":"2026-02-22","permalink":"/nginx-config-tests/","summary":"TL;DR Put your nginx config (mostly contents of /etc/nginx) under version control (private repository) Use the official Gixy docker image to test your configuration with Gixy Use docker to verify your nginx config is working with nginx -t. You will need to craft a Dockerfile that can be different depending on your needs Optional: Simplify your local workflow with a justfile and handy recipes like test-nginx-gixy or test-nginx-t What is Gixy? And what does it test? You can think of Gixy as a static nginx config analyzer. As stated on the official website, Gixy reviews nginx configuration for security risks, misconfigurations, and missed hardening opportunities.\n","title":"Nginx config testing with Gixy"},{"content":" Tip If you are new to Marp, check out this article that covers the basics: Marp: Create Presentations using Markdown\nIf you did not use just before, you can refer to this article: My Favorite FOSS Tools: Just\nTL;DR Prerequisites You version your Marp presentations on GitHub You have Actions enabled in your repository You are somewhat familiar with the just command runner and GitHub workflows Setup version your presentation markdown files in a GitHub repository use the marp CLI tooling with in a just recipe to build presentations add a GitHub workflow with paths-ignore setting to only build presentations you are working on include to read the paths-ignore config from the github.ci.yaml into your build recipe Project Structure . ├── dist │ └── .gitkeep ├── .editorconfig │ └── workflows │ └── github.ci.yaml ├── .gitignore ├── justfile ├── node_modules ├── package.json ├── pnpm-lock.yaml ├── README.md └── src ├── marp-structure │ └── index.md └── testing-strategy └── index.md Warning The CI settings as well as the just recipes depend on the directory structure. The following code and explanations are based on the setup above. If your setup differs, you need to adjust the paths used in the github.ci.yaml and the justfile.\nRelevant folders and files dist\nThis is the target directory for built presentations. The .gitkeep was added to have the empty folder versioned so I could test my CI builds.\nsrc\nInside source, I chose to version every presentation in its own folder, which is named descriptively. The index.md file in each folder inside src holds the actual marp markdown presentation.\njustfile\nI like utilizing the flexibility of just as a command runner. In the justfile, I defined recipes to build my presentations like \u0026ldquo;build-all\u0026rdquo;, to build all presentations or \u0026ldquo;build-active\u0026rdquo;, to only build the ones I am currently working on.\ngithub.ci.yaml on: push: branches: - main paths-ignore: - README.md - .github/workflows/github.ci.yaml - justfile - package.json - .gitignore - .editorconfig - src/marp-structure/** concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v6 - name: Install just uses: extractions/setup-just@v3 - name: Install Fish Shell run: sudo apt-add-repository ppa:fish-shell/release-3;sudo apt install fish - name: Setup Node uses: actions/setup-node@v6 with: node-version: \u0026#39;24\u0026#39; - name: Install pnpm uses: pnpm/action-setup@v4 - name: Install project dependencies run: just install - name: build active PDFs run: just build-active - name: Commit Artifacts uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Build PDF Click to view detailed explanations about the workflow file Section 1: Triggers on: push: branches: - main paths-ignore: - README.md - .github/workflows/github.ci.yaml - justfile - package.json - .gitignore - .editorconfig - src/marp-structure/** # ... (more presentation directories) The workflow runs whenever code is pushed to the main branch. paths-ignore however disables the trigger for the specified paths. This comes in handy to save resources and time. When the README or configuration changes, there is no need to build the presentations. I also use this setting to stop building presentations that are already finished.\nSection 2: Concurrency Control concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true This configuration controls how multiple workflow runs are handled. The group directive groups workflows by name and branch reference. cancel-in-progress: true leads to the cancellation of workflows that are still running, if a new workflow is triggered. This makes sense, as the newer status quo will \u0026ldquo;win\u0026rdquo; anyways regarding generated presentations.\nSection 3: The Job Definition jobs: build: runs-on: ubuntu-latest This section defines the job(s) that are carried out by the workflow. You can chose a name you see fit. Since we are building presentations (PDFs) I chose \u0026ldquo;build\u0026rdquo;. And this job will run on a free GitHub hosted Ubuntu runner.\nSection 4: The Steps Each step runs sequentially to prepare the environment and build the PDFs:\nsteps: - name: Checkout Code uses: actions/checkout@v6 Downloads your repository code. The uses keyword references a pre-built action from the GitHub Marketplace.\n- name: Install just uses: extractions/setup-just@v3 Installs the just command runner used to execute build commands from the justfile.\n- name: Install Fish Shell run: sudo apt-add-repository ppa:fish-shell/release-3;sudo apt install fish Installs Fish shell because the justfile recipes use Fish syntax. The run keyword executes shell commands directly.\n- name: Setup Node uses: actions/setup-node@v6 with: node-version: \u0026#39;24\u0026#39; Installs Node.js v24, needed for the Marp and Mermaid tools. The with keyword passes parameters to actions.\n- name: Install pnpm uses: pnpm/action-setup@v4 Installs pnpm, the package manager this project uses.\n- name: Install project dependencies run: just install Runs pnpm install to install the npm packages (marp-cli, mermaid-cli) needed to build presentations.\n- name: build active PDFs run: just build-active Builds PDFs for non-ignored presentations, converting Markdown files to PDFs in the dist/ folder.\n- name: Commit Artifacts uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Build PDF Automatically commits the rebuilt PDFs back to the repository, keeping them up-to-date.\nInfo This workflow is based on the tooling I use. You do not have to use pnpm, fish shell or just. Node comes with npm and you can write shell or bash-scripts in your justfile, too. If you do not want to use just, you can also write scripts and run them directly in your github workflow by specifying run: myscript.sh.\njustfile # run pnpm install install: pnpm install # build only active presentation PDFs (not in CI paths-ignore) build-active: #!/usr/bin/env fish # Extract ignored presentation paths from GitHub CI config set -l ignored_presentations (awk -F\u0026#39;/\u0026#39; \u0026#39;/src\\/.*\\/\\*\\*/ {print $2}\u0026#39; .github/workflows/github.ci.yaml | grep -v assets) for slide in src/*/index.md set -l dirname (path dirname \u0026#34;$slide\u0026#34;) set -l presentation (path basename \u0026#34;$dirname\u0026#34;) # Skip if presentation is in ignored list if contains $presentation $ignored_presentations echo \u0026#34;Skipping $presentation (ignored in CI config)\u0026#34; continue end pushd \u0026#34;$dirname\u0026#34; just build \u0026amp; popd end wait Click to view detailed explanations about the justfile Recipe: install # run pnpm install install: pnpm install Installs the project dependencies defined in the package.json.\nRecipe: build-active # build only active presentation PDFs (not in CI paths-ignore) build-active: #!/usr/bin/env fish # Extract ignored presentation paths from GitHub CI config set -l ignored_presentations (awk -F\u0026#39;/\u0026#39; \u0026#39;/src\\/.*\\/\\*\\*/ {print $2}\u0026#39; .github/workflows/github.ci.yaml | grep -v assets) for slide in src/*/index.md set -l dirname (path dirname \u0026#34;$slide\u0026#34;) set -l presentation (path basename \u0026#34;$dirname\u0026#34;) # Skip if presentation is in ignored list if contains $presentation $ignored_presentations echo \u0026#34;Skipping $presentation (ignored in CI config)\u0026#34; continue end pushd \u0026#34;$dirname\u0026#34; just build \u0026amp; popd end wait Builds PDF files for presentations that aren\u0026rsquo;t marked as ignored in the CI configuration.\nThe process:\nReads CI config - Extracts presentation names from the paths-ignore list in .github/workflows/github.ci.yaml Finds all presentations - Looks for index.md files in src/*/ directories Filters - Skips presentations that are in the ignored list Builds in parallel - Runs just build for each active presentation (the \u0026amp; runs them simultaneously) Waits - Uses wait to ensure all builds complete before finishing ","date":"2026-02-21","permalink":"/marp-automate-presentation-builds/","summary":" Tip If you are new to Marp, check out this article that covers the basics: Marp: Create Presentations using Markdown\nIf you did not use just before, you can refer to this article: My Favorite FOSS Tools: Just\n","title":"Marp: Automate presentation builds"},{"content":"TL;DR Installation Arch Linux:\ninstall texlive-binextra: sudo pacman -Syu texlive-binextra install texlive-latexrecommended: sudo pacman -Syu texlive-latexrecommended Ubuntu (tested on 25.04):\ninstall texlive-extra-utils: sudo apt install texlive-extra-utils Usage pdfjam --nup 2x2 -i infile.pdf -o outfile.pdf - generates a new pdf with a 2x2 grid of the pages of the input pdf per page Most useful options Option Example Effect Extra --landscape/ --no-landscape --landscape landscape/portrait format --nup axb with a and b being integers --nup 2x3 multiple (input)pages per (output)page --paper spec --paper a4paper or --a4paper control scaling/sizing common formats: a0paper, a1paper, a2paper, a3paper, a4paper, a5paper, a6paper,b0paper, b1paper, b2paper, b3paper, b4paper, b5paper, b6paper,c0paper, c1paper, c2paper, c3paper, c4paper, c5paper, c6paper,b0j, b1j, b2j, b3j, b4j, b5j, b6j,ansiapaper, ansibpaper, ansicpaper, ansidpaper, ansiepaper,letterpaper, executivepaper, legalpaper (source) ","date":"2026-01-24","permalink":"/favorite-foss-tools-pdfjam/","summary":"TL;DR Installation Arch Linux:\ninstall texlive-binextra: sudo pacman -Syu texlive-binextra install texlive-latexrecommended: sudo pacman -Syu texlive-latexrecommended Ubuntu (tested on 25.04):\ninstall texlive-extra-utils: sudo apt install texlive-extra-utils Usage pdfjam --nup 2x2 -i infile.pdf -o outfile.pdf - generates a new pdf with a 2x2 grid of the pages of the input pdf per page Most useful options Option Example Effect Extra --landscape/ --no-landscape --landscape landscape/portrait format --nup axb with a and b being integers --nup 2x3 multiple (input)pages per (output)page --paper spec --paper a4paper or --a4paper control scaling/sizing common formats: a0paper, a1paper, a2paper, a3paper, a4paper, a5paper, a6paper,b0paper, b1paper, b2paper, b3paper, b4paper, b5paper, b6paper,c0paper, c1paper, c2paper, c3paper, c4paper, c5paper, c6paper,b0j, b1j, b2j, b3j, b4j, b5j, b6j,ansiapaper, ansibpaper, ansicpaper, ansidpaper, ansiepaper,letterpaper, executivepaper, legalpaper (source) ","title":"My Favorite FOSS Tools: pdfjam"},{"content":"TL;DR introduce a new setting to your front-matter \u0026ldquo;index\u0026rdquo; when set to true, the page should be indexed use it in head template at layouts/partials/head/head.html to generate the according meta robots tag {{- if .Params.index }} \u0026lt;meta name = \u0026#34;robots\u0026#34; content = \u0026#34;index\u0026#34;\u0026gt; {{- else }} \u0026lt;meta name = \u0026#34;robots\u0026#34; content = \u0026#34;noindex\u0026#34;\u0026gt; {{- end }} if you are using a theme, copy their head.html and adjust it accordingly. Hugo will use your template when building for prod but a head.html partial usually contains more than just the meta robots tag Warning Keep in mind: This will lead to all content not being indexed and only content being indexed where you specify \u0026ldquo;index = true\u0026rdquo; in your front matter. This also means that content automatically generated by Hugo (i.e. content you do not manually curate a template for) will not be indexed.\nFurther reading \u0026ldquo;Robots\u0026rdquo; - Control what is indexed\n","date":"2026-01-04","permalink":"/hugo-control-site-indexing/","summary":"TL;DR introduce a new setting to your front-matter \u0026ldquo;index\u0026rdquo; when set to true, the page should be indexed use it in head template at layouts/partials/head/head.html to generate the according meta robots tag {{- if .Params.index }} \u0026lt;meta name = \u0026#34;robots\u0026#34; content = \u0026#34;index\u0026#34;\u0026gt; {{- else }} \u0026lt;meta name = \u0026#34;robots\u0026#34; content = \u0026#34;noindex\u0026#34;\u0026gt; {{- end }} if you are using a theme, copy their head.html and adjust it accordingly. Hugo will use your template when building for prod but a head.html partial usually contains more than just the meta robots tag Warning ","title":"Control site indexing in Hugo with \u003cmeta name=\"robots\"\u003e"},{"content":"TL;DR Create a sitemap.xml at layouts/_default/sitemap.xml Insert the skeleton and fill it with the content you want to be discovered, using Hugo template syntax. {{ printf \u0026#34;\u0026lt;?xml version=\\\u0026#34;1.0\\\u0026#34; encoding=\\\u0026#34;utf-8\\\u0026#34; standalone=\\\u0026#34;yes\\\u0026#34;?\u0026gt;\u0026#34; | safeHTML }} \u0026lt;urlset xmlns=\u0026#34;http://www.sitemaps.org/schemas/sitemap/0.9\u0026#34; xmlns:xhtml=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34;\u0026gt; YOUR CONTENT GOES HERE \u0026lt;/urlset\u0026gt; Include the sitemap in the robots.txt User-agent: * Allow: / Sitemap: https://miriam-mueller.com/sitemap.xml Sitemap?! A sitemap is a file that helps search engines discover your site. You do not need a sitemap as search engine bots crawl your site and follow links on your site. A sitemap can aid the discovery however, especially if your site is new and has few other pages linking to it.\nKey Knowledge A sitemap only aids search engine in discovering your site It does not control what is indexed (or not indexed) when is a sitemap helpful? large sites isolated pages with few links to each other new sites quick and frequent content changes sites excluded from indexing (robots.txt, robots-meta tag) should not be in the sitemap most Hugo themes automatically generate the sitemap.xml. Check it, if it matches your desired result Custom sitemap.xml full example {{ printf \u0026#34;\u0026lt;?xml version=\\\u0026#34;1.0\\\u0026#34; encoding=\\\u0026#34;utf-8\\\u0026#34; standalone=\\\u0026#34;yes\\\u0026#34;?\u0026gt;\u0026#34; | safeHTML }} \u0026lt;urlset xmlns=\u0026#34;http://www.sitemaps.org/schemas/sitemap/0.9\u0026#34; xmlns:xhtml=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34;\u0026gt; {{ range .Data.Pages }} {{ if not (or (strings.Contains .RelPermalink \u0026#34;/tags/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/year/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/series/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/search/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/contact/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/archives/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/about/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/projects/\u0026#34;) (strings.Contains .RelPermalink \u0026#34;/privacy-policy/\u0026#34;) (eq .Permalink \u0026#34;https://miriam-mueller.com/post/\u0026#34;) (eq .Permalink \u0026#34;https://miriam-mueller.com/page/\u0026#34;) ) }} \u0026lt;url\u0026gt; \u0026lt;loc\u0026gt;{{ .Permalink }}\u0026lt;/loc\u0026gt; {{ if not .Lastmod.IsZero }} \u0026lt;lastmod\u0026gt;{{ safeHTML ( .Lastmod.Format \u0026#34;2006-01-02T15:04:05-07:00\u0026#34; ) }}\u0026lt;/lastmod\u0026gt; {{ end }} \u0026lt;/url\u0026gt; {{ end }} {{ end }} \u0026lt;/urlset\u0026gt; Custom sitemap.xml explained For my blog, I decided that I only want to index content in the form of articles. In order to do so, I am using the \u0026lt;meta name=\u0026quot;robots\u0026quot;\u0026gt; HTML meta-tag. Because of the Hugo themes generating a sitemap including automatically generated pages like the archive or tag overviews, I also started having my own sitemap.xml template that only includes the actual posts.\nBecause we are in the context of Hugo, we can use Hugo template syntax to iterate over all pages.\n{{ range .Data.Pages }}: this range includes all pages, also the automatically generated ones.\n{{if not (or...: do not do whatever comes after the if, if one of the things inside the or is truthy\n(strings.Contains .RelPermalink \u0026quot;/xyz\u0026quot;): if the URL of the page considered during the loop contains xyz, it is one of the pages nothing will be done for in the if-block.\n(eq .Permalink \u0026quot;https://whatever.you.chose/post/: if the URL of the page considered during the loop is exactly matching, nothing will be done in the if-block\ncontent that is written to sitemamp.xml if we have a page we want to have in there:\n\u0026lt;url\u0026gt; \u0026lt;loc\u0026gt;{{ .Permalink }}\u0026lt;/loc\u0026gt; {{ if not .Lastmod.IsZero }} \u0026lt;lastmod\u0026gt;{{ safeHTML ( .Lastmod.Format \u0026#34;2006-01-02T15:04:05-07:00\u0026#34; ) }}\u0026lt;/lastmod\u0026gt; {{ end }} \u0026lt;/url\u0026gt; resulting sitemap: https://miriam-mueller.com/sitemap.xml Further reading \u0026amp; videos \u0026ldquo;Robots\u0026rdquo; explained: HTML meta-tag and HTTP Response Header Google Developers Sitemap Article Hostinger Sitemap Tutorial Google Search Central - 3 tips for setting up a sitemap Google Search Central - Google Search Console Training ","date":"2026-01-04","permalink":"/hugo-custom-sitemap/","summary":"TL;DR Create a sitemap.xml at layouts/_default/sitemap.xml Insert the skeleton and fill it with the content you want to be discovered, using Hugo template syntax. {{ printf \u0026#34;\u0026lt;?xml version=\\\u0026#34;1.0\\\u0026#34; encoding=\\\u0026#34;utf-8\\\u0026#34; standalone=\\\u0026#34;yes\\\u0026#34;?\u0026gt;\u0026#34; | safeHTML }} \u0026lt;urlset xmlns=\u0026#34;http://www.sitemaps.org/schemas/sitemap/0.9\u0026#34; xmlns:xhtml=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34;\u0026gt; YOUR CONTENT GOES HERE \u0026lt;/urlset\u0026gt; Include the sitemap in the robots.txt User-agent: * Allow: / Sitemap: https://miriam-mueller.com/sitemap.xml Sitemap?! A sitemap is a file that helps search engines discover your site. You do not need a sitemap as search engine bots crawl your site and follow links on your site. A sitemap can aid the discovery however, especially if your site is new and has few other pages linking to it.\n","title":"How to create a custom sitemap for your Hugo blog"},{"content":"TL;DR in the \u0026lt;head\u0026gt; section of a page\u0026rsquo;s HTML you can use the tag \u0026lt;meta name=\u0026ldquo;robots\u0026rdquo;\u0026gt; to control if a search engine should index the site \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;index\u0026gt; will lead to the resource being indexed \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;noindex\u0026gt; will lead to the resource not being indexed the X-Robots-Tag response header may also define whether a resource should be indexed or not it is especially useful for non-HTML documents (as they cannot define a robots HTML tag) add X-Robots-Tag in nginx: add_header X-Robots-Tag \u0026quot;index\u0026quot; add X-Robots-Tag in apache: Header Set X-Robots-Tag \u0026quot;index\u0026quot; or RequestHeader append X-Robots-Tag \u0026quot;index\u0026quot; in order for the rules to work, the resource (i.e. a page or resource on the server like a PDF) must not be excluded from crawling via the robots.txt robots.txt The robots.txt must be in the root path of your directory. Mine is at https://miriam-mueller.com/robots.txt. Those text files are simple and contain text in a specified format, most bots in the internet understand. There is no mechanism enforcing that bots must follow the rules you put in the robots.txt, but most \u0026ldquo;friendly\u0026rdquo; bots respect it.\ndo not use robots.txt to control indexing If you use the \u0026ldquo;Disallow\u0026rdquo; rule in the robots.txt for example a search crawler will not access it. That does not mean, that it will not be indexed. If the bot encounters a link to the page you disallowed, but then cannot access it, it cannot read the info from a robots meta-tag or X-Robots-Tag header and the site might end up being indexed.\nRobots meta-tag configuration explained When using the robots meta-tag there are two main configurations you need to understand: index and noindex, as well as follow and nofollow. Index and noindex control, whether you want to index a resource. Follow and nofollow are used to indicate, if the search engine crawler should follow links on that resource.\nSee also: mdn \u0026lt;meta name=\u0026ldquo;robots\u0026rdquo;\nConfiguration Consequence Use Case \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;index\u0026quot;\u0026gt; The page is indexed you want the resource to be indexed \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;noindex\u0026quot;\u0026gt; The page is not indexed you do not want the resource to be indexed \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;noindex, follow\u0026quot;\u0026gt; The page is not indexed; the crawler follows links when having many internal links, you want the crawler to follow those links but they might not be of use to the user and thus should not be indexed \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;index, nofollow\u0026quot;\u0026gt; The page is indexed; the crawler does not follow links if you are running a forum for example and you do not want the bot to follow links users post in the comments Fine tune link following Instead of using the robots meta-tag, you can also specify on a single link, if it should be/not be followed. This is done via the \u0026ldquo;rel\u0026rdquo; attribute. In 2019, an update was made to the rel-attributes and how they are treated in search engine crawlers.\n\u0026lt;a href=\u0026#34;https://something.com rel=\u0026#34;nofollow\u0026#34;\u0026gt; See also: mdn - HTML attribute: rel\nNofollow - a recommendation When first introduced, nofollow meant what it spells: do not follow. The above-mentioned change in 2019, as outlined in this Google Search Central Article on \u0026ldquo;Evoling \u0026rsquo;nofollow\u0026rsquo;\u0026rdquo; article, lead to the current status quo. It is, that the search algorithms treat all rel-attributes as hints and the content will be processed.\nX-Robots-Tag response header This header is not part of any specification, but it is the de-facto standard method for telling a search engine crawler whether to index a resource or not. Syntax:\nX-Robots-Tag: \u0026lt;indexing-rule\u0026gt; X-Robots-Tag: \u0026lt;indexing-rule\u0026gt;, …, \u0026lt;indexing-ruleN\u0026gt; See also: mdn X-Robots-Tag header\nFurther reading Google Search Central - Introduction to robots.txt Google Search Central - Robots meta tag, data-nosnippet, and X-Robots-Tag specifications German:\nSistrix - Nofollow und Follow: wofür werden sie genutzt? Sistrix - rel=\u0026ldquo;sponsored\u0026rdquo; und rel=\u0026ldquo;ugc\u0026rdquo; Sistrix - rel-Attribute: Wie nutze ich sie für bessere SEO? ","date":"2026-01-04","permalink":"/robots-control-what-is-indexed/","summary":"TL;DR in the \u0026lt;head\u0026gt; section of a page\u0026rsquo;s HTML you can use the tag \u0026lt;meta name=\u0026ldquo;robots\u0026rdquo;\u0026gt; to control if a search engine should index the site \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;index\u0026gt; will lead to the resource being indexed \u0026lt;meta name=\u0026quot;robots\u0026quot; content=\u0026quot;noindex\u0026gt; will lead to the resource not being indexed the X-Robots-Tag response header may also define whether a resource should be indexed or not it is especially useful for non-HTML documents (as they cannot define a robots HTML tag) add X-Robots-Tag in nginx: add_header X-Robots-Tag \u0026quot;index\u0026quot; add X-Robots-Tag in apache: Header Set X-Robots-Tag \u0026quot;index\u0026quot; or RequestHeader append X-Robots-Tag \u0026quot;index\u0026quot; in order for the rules to work, the resource (i.e. a page or resource on the server like a PDF) must not be excluded from crawling via the robots.txt robots.txt The robots.txt must be in the root path of your directory. Mine is at https://miriam-mueller.com/robots.txt. Those text files are simple and contain text in a specified format, most bots in the internet understand. There is no mechanism enforcing that bots must follow the rules you put in the robots.txt, but most \u0026ldquo;friendly\u0026rdquo; bots respect it.\n","title":"\"Robots\": Control what search engines index"},{"content":"Quickstart pdfunite is a command line utility it is part of \u0026ldquo;Poppler\u0026rdquo;, a PDF rendering library Install poppler: Arch Linux: pacman -Syu poppler Ubuntu: apt install poppler-utils MacOS: brew install poppler create several pdf files merge them into one output file: pdfunite input1.pdf input2.pdf inputN.pdf output.pdf ","date":"2025-11-28","permalink":"/favorite-foss-tools-pdfunite/","summary":"Quickstart pdfunite is a command line utility it is part of \u0026ldquo;Poppler\u0026rdquo;, a PDF rendering library Install poppler: Arch Linux: pacman -Syu poppler Ubuntu: apt install poppler-utils MacOS: brew install poppler create several pdf files merge them into one output file: pdfunite input1.pdf input2.pdf inputN.pdf output.pdf ","title":"My Favorite FOSS Tools: pdfunite (poppler)"},{"content":"TL;DR Check out https://github.com/openai/agents.md Migrate your existing AI agent instruction files create an AGENTS.md file at the root of your repository other AI instruction files are symbolic links to the AGENTS.md at the root level of the project Where does this proposal come from? Originally, the Amp Code team started supporting an AGENT.md file for their AI coding agents (https://ampcode.com/news/AGENTS.md). Shortly later, OpenAI picked that up but in the plural form. As stated on the agents.md project website, AGENTS.md \u0026ldquo;emerged from a collaborative effort across the AI software development ecosystem\u0026rdquo;.\nWhy? Instead of maintaining separate configurations, you only maintain one. This was the case already if you used the mechanism outlined in my previous article \u0026ldquo;AI Instructions in Code Repositories\u0026rdquo;. But now the file at root level has the naming convention \u0026ldquo;AGENTS.md\u0026rdquo;, which allows companies following this open format to offer built-in support for that file in their agent.\nIt saves you the time of telling your coding agent which file to use.\n","date":"2025-11-02","permalink":"/ai-instructions-agents-md/","summary":"TL;DR Check out https://github.com/openai/agents.md Migrate your existing AI agent instruction files create an AGENTS.md file at the root of your repository other AI instruction files are symbolic links to the AGENTS.md at the root level of the project Where does this proposal come from? Originally, the Amp Code team started supporting an AGENT.md file for their AI coding agents (https://ampcode.com/news/AGENTS.md). Shortly later, OpenAI picked that up but in the plural form. As stated on the agents.md project website, AGENTS.md \u0026ldquo;emerged from a collaborative effort across the AI software development ecosystem\u0026rdquo;.\n","title":"AI Instructions: Agents.md"},{"content":"TL;DR Checkout the project\u0026rsquo;s GitHub: https://github.com/casey/just and official guide Install just: Arch Linux: pacman -Syu just Ubuntu: apt install just MacOS: brew install just checkout the README for detailed info create a justfile: touch justfile Get started: open the justfile and insert: # List all available recipes [private] default: just --list # echos \u0026#34;hello\u0026#34; echo-hello: echo \u0026#34;Hello\u0026#34; type just echo-hello in your terminal and press enter type just in your terminal and press enter Exemplary Content Explained # List all available recipes [private] default: just --list # echos \u0026#34;hello\u0026#34; echo-hello: echo \u0026#34;Hello\u0026#34; lines with hashes are the info listed when \u0026ldquo;just \u0026ndash;list\u0026rdquo; is executed [private] leads to the recipe not being displayed when you execute \u0026ldquo;just \u0026ndash;list\u0026rdquo; default is the default recipe executed when you only type \u0026ldquo;just\u0026rdquo; and press enter. We set it to \u0026ldquo;just \u0026ndash;list\u0026rdquo; which lists all available commands \u0026ldquo;echo-hello\u0026rdquo; is a recipe Output of \u0026ldquo;just\u0026rdquo; and \u0026ldquo;just echo-hello\u0026rdquo; - what is what? When you entered \u0026ldquo;just\u0026rdquo;, the command defined by the recipe \u0026ldquo;default\u0026rdquo; was executed: \u0026ldquo;just \u0026ndash;list\u0026rdquo;. In consequence, all available recipes are listed. We see that \u0026ldquo;echo-hello\u0026rdquo; is available. Behind it we see our explanatory comment that says that the recipe \u0026ldquo;echo-hello\u0026rdquo; echoes \u0026ldquo;hello\u0026rdquo;.\nWhen we executed the recipe \u0026ldquo;echo-hello\u0026rdquo;, we see the command that was executed by the recipe \u0026ldquo;echo \u0026lsquo;Hello\u0026rsquo;\u0026rdquo; and its output, which is a plain \u0026ldquo;Hello\u0026rdquo;.\nAttributes We already got to know the [default] and [private] attribute earlier. There are more helpful attributes you can use:\nattribute description [confirm] leads to the need for the user to confirm the recipe execution [no-cd] by default, recipes run in the working directory set to the directory that contains the justfile. no-cd leads to the working directory being set to the directory where just was invoked in [working-directory: \u0026lsquo;somedir\u0026rsquo;] you can override the directory for a specific recipe [doc(\u0026rsquo;the documentation\u0026rsquo;)] add documentation for a recipe but the documentation will not show as description when running just --list There a even more attributes in the official documentation\nFacts \u0026amp; Features Some facts.. the syntax of just was inspired by make just is a command runner however, not a build system Linux, MacOS and Windows are supported without additional dependencies a GitHub Action is available: \u0026ldquo;extractions/setup-just@v3\u0026rdquo; starting from version 1.0 the maintainers commited themselves to ensure backwards compatibility syntax highlighting in IDEs is supported via plugins. Due to its inspiration from make, a makefile syntax highlighter also works for just you can chain recipes. I often do: just install start (install project dependencies and start project. Those are custom recipes I wrote for my projects, they are not predefined by just) you can extend a general justfile with a custom one by importing another justfile at the top of your default justfile (pay respect to duplicate recipes etc. See the available settings for more information) \u0026hellip;and Features Feature Description Command Line Arguments Recipes can accept command line arguments. Environment Variables just loads .env files; access them with the env(key) function. Aliases just supports aliases. Concatenation just supports concatenation with \u0026ldquo;+\u0026rdquo;. Logical Operators just supports logical operators (\u0026amp;\u0026amp; and ||) Conditional Statements just supports conditional statements. Built-in Functions Built-in functions are available for just and OS-related topics, and also for string manipulation and many more. Shell Support You can write shell in just. Predefined Constants \u0026amp; Imports You can extend a general justfile with a custom one by importing another justfile at the top of your default justfile (pay respect to duplicate recipes, etc. See the available settings for more information). I only listed the sections I needed most during my career, but I think the whole README is worth taking a look if you start using just.\n","date":"2025-09-05","permalink":"/favorite-foss-tools-just/","summary":"TL;DR Checkout the project\u0026rsquo;s GitHub: https://github.com/casey/just and official guide Install just: Arch Linux: pacman -Syu just Ubuntu: apt install just MacOS: brew install just checkout the README for detailed info create a justfile: touch justfile Get started: open the justfile and insert: # List all available recipes [private] default: just --list # echos \u0026#34;hello\u0026#34; echo-hello: echo \u0026#34;Hello\u0026#34; type just echo-hello in your terminal and press enter type just in your terminal and press enter Exemplary Content Explained # List all available recipes [private] default: just --list # echos \u0026#34;hello\u0026#34; echo-hello: echo \u0026#34;Hello\u0026#34; lines with hashes are the info listed when \u0026ldquo;just \u0026ndash;list\u0026rdquo; is executed [private] leads to the recipe not being displayed when you execute \u0026ldquo;just \u0026ndash;list\u0026rdquo; default is the default recipe executed when you only type \u0026ldquo;just\u0026rdquo; and press enter. We set it to \u0026ldquo;just \u0026ndash;list\u0026rdquo; which lists all available commands \u0026ldquo;echo-hello\u0026rdquo; is a recipe Output of \u0026ldquo;just\u0026rdquo; and \u0026ldquo;just echo-hello\u0026rdquo; - what is what? ","title":"My Favorite FOSS Tools: Just"},{"content":"TL;DR checkout PintaProject on GitHub Pinta is available for Windows, macOS and Linux you can either download it or install it via Flatpak, Snap or your package manager (if your distribution includes it) Installing Pinta On Arch Linux, pinta is available via tha AUR. You can refer to this guide if the AUR is new for you: How to install AUR packages. Additionally, the homepage of pinta provides links to official documentation on how to install it. On Ubuntu, you can search it via the software center and install it via the GUI. For MacOS, pinta is available via homebrew.\nWhat does Pinta look like? What can be done with it? As you can see in the screenshot, Pinta comes with all the basic drawing tools you might need, like pencil and brush, drawing shapes (among others rectangle and circle), selection/cutting, an eraser or writing text. Pinta also supports layers and a history of actions. In the top right corner you also see some general tools like applying filters or mirroring the image.\nWhy Pinta? I use it in my daily work whenever I need to highlight information on an image or screenshot. Pinta is lightweight, so it starts up fast and gives me the basic tools I need.\n","date":"2025-09-05","permalink":"/favorite-foss-tools-pinta/","summary":"TL;DR checkout PintaProject on GitHub Pinta is available for Windows, macOS and Linux you can either download it or install it via Flatpak, Snap or your package manager (if your distribution includes it) Installing Pinta On Arch Linux, pinta is available via tha AUR. You can refer to this guide if the AUR is new for you: How to install AUR packages. Additionally, the homepage of pinta provides links to official documentation on how to install it. On Ubuntu, you can search it via the software center and install it via the GUI. For MacOS, pinta is available via homebrew.\n","title":"My Favorite FOSS Tools: Pinta"},{"content":"TL;DR check out the fish shell and tide fish shell offers a decent auto-completion fish scripts are easy to write and read extensive documentation available on Linux and MacOS tide: easy configuration of the look and feel with a configuration wizard GitHub: https://github.com/fish-shell/fish-shell https://github.com/IlanCosman/tide For Beginners: Shell?! Taken from the Gnome help center:\nInfo \u0026lsquo;A terminal is a text input point in a computer that is also called the Command Line Interface (CLI).\u0026rsquo;\nSo the terminal is what you actually see, the GUI. You type \u0026ldquo;cd\u0026rdquo; in the terminal and hit enter. From there on, the shell gets to work.\nInfo \u0026lsquo;A shell is a program that provides an interface to invoke or “launch” commands or another program inside a terminal. It also allows you to view and browse the contents of directories. Popular shells include bash, zsh, fish.\u0026rsquo;\nThe shell is what translates the commands you entered in the terminal into something your operating system\u0026rsquo;s kernel can understand and carry out. For example switching directories when entering \u0026ldquo;cd\u0026rdquo;.\nSome shells you might have heard of are for example bash and zsh. On top of those two technologies, there exist toolings, of which you might have heard, such as oh my zsh.\nIn the case of fish, a useful tooling is tide.\nSwitching to Fish The official documentation got us covered. In the section \u0026ldquo;Installation\u0026rdquo; you can find instructions on how to install the fish shell and set it up as default shell. You can also refer to your operating system\u0026rsquo;s documentation, of which some are listed below:\nArch Linux Ubuntu MacOS NixOS Installing Tide If you chose to use Tide, you need to install it. The simplest way is to use Fisher, a plugin manager for the fish shell. In Tide\u0026rsquo;s README, there is also a section for manual installation.\nFish Shell (\u0026amp; Tide) Setup - Configuration Wizards Fish shell as well as tide come with graphical configuration wizards. While the fish shell one is web-based, tide comes with a terminal based wizard.\nstart fish configuration wizard: fish_config start tide configuration wizard. tide configure For a first setup, it is enough to click through and try things out.\nMy Most Beloved Features command suggestions: as per the official website \u0026ldquo;fish suggests commands as you type based on history and completions\u0026rdquo;. During my everday work I tend do use a set of commands in the same order very often. Fish saves me a ton of time because based on what I have used in the past, I do not have to type in lengthy commands all the time. The right command is only a tab key away! beginner friendly setup: Yes, you can have the opinion, that you just \u0026ldquo;need to deal with it\u0026rdquo; and learn whatever it takes for your job (or passion) to succeed. I agree to a certain point but no matter if you get started learning/studying informatics, or if you enter the field as a cross-background beginner like I did, there is a lot to of input. You can make your life easier by starting with something easy and working your way from there. The configuration wizards and the extensive documentation make fish shell a very decent shell for me. readable scripts: let me be honest, I did not write a lot of complex zsh or bash scripts in my life so far. Having a cross background coming from biology, I only learned Python and JavaScript at university. Later, I often missed the way Python works out regarding the readability of scripts. In biology, if you can read English, you can read most of the Python scripts required for your daily work. With fish scripts, it is almost the same! I can write if/elses, switch/cases, functions etc. the way I am basically used to. Good bye \u0026ldquo;if/fi\u0026rdquo; and \u0026ldquo;for..do\u0026hellip;done\u0026rdquo;! Showcase: Completion Since I have been writing a bit more recently in my blog, this is the suggestion I get when being in my home directory: After switching into the directory if my blog, you might think \u0026ldquo;maybe it always suggests the \u0026ldquo;cd\u0026rdquo; command with the highest count. But it does not. Inside my blog\u0026rsquo;s directory, \u0026ldquo;..\u0026rdquo; for moving one directory up is more likely due to the history of my command usage: Exemplary Function with Completion A Simple \u0026ldquo;Say Hello\u0026rdquo; Function Script location: /home/youruser/.config/fish/functions\nfunction say_hello --argument language switch \u0026#34;$language\u0026#34; case en echo \u0026#34;Hello World!\u0026#34; case es echo \u0026#34;¡Hola Mundo!\u0026#34; case fr echo \u0026#34;Bonjour le monde!\u0026#34; case de echo \u0026#34;Hallo Welt!\u0026#34; case \u0026#39;*\u0026#39; echo \u0026#34;Hello World! (Language not supported)\u0026#34; end end How it works:\n--argument language: This assigns the positional argument $argv[1] to the more readable variable language.\nswitch \u0026quot;$language\u0026quot;: This block checks the value of the language variable.\ncase en, case es, etc.: Each case handles a specific language code and prints the corresponding greeting.\ncase '*': This is a wildcard case that handles any input that doesn\u0026rsquo;t match the other cases, providing a default message.\nGenerating a Completion for the \u0026ldquo;Say Hello\u0026rdquo; Function Script location: /home/youruser/.config/fish/completions\ncomplete -c say_hello -f -a \u0026#39;en es fr de\u0026#39; -d \u0026#34;Language\u0026#34; How it works:\ncomplete -c say_hello: Specifies that this completion script is for the say_hello command.\n-f : When you create a custom completion for a command, fish often tries to offer filenames as well. -f prevents that, ensuring only your specified arguments are offered.\n-a 'en es fr de' -d \u0026quot;Language\u0026quot;: -a adds the argument and -d provides a description that will appear when you press tab.\nExtra Nerd Knowledge Fish is a project that survived a rewrite succesfully. Read more about the Rust port in their official blog: https://fishshell.com/blog/rustport/\n","date":"2025-08-17","permalink":"/favorite-foss-tools-fish-shell/","summary":"TL;DR check out the fish shell and tide fish shell offers a decent auto-completion fish scripts are easy to write and read extensive documentation available on Linux and MacOS tide: easy configuration of the look and feel with a configuration wizard GitHub: https://github.com/fish-shell/fish-shell https://github.com/IlanCosman/tide For Beginners: Shell?! Taken from the Gnome help center:\nInfo ","title":"My Favorite FOSS Tools: Fish Shell"},{"content":"TL;DR Place general ai instructions in your repository, for example at src/docs/ai/ai-instructions.md Specific instructions per coding assitant are symlinks to your general ai-instructions.md AI Coding Assistants? In recent years, generative artificial intelligence has become a part of everyday life. Apart from text, image and video generation, there also exist AI coding agents. The \u0026ldquo;I\u0026rdquo; for \u0026ldquo;intelligence\u0026rdquo; in AI is slightly misleading in that context. You need to give coding assistants precise instructions so they carry out tasks the way you want them to.\nAI Assistant Instructions? Different coding assitants require differnt instructions. While GitHub Copilot needs a copilot-instructions.md in the .github folder of a repository, windsurf and cursor need a \u0026ldquo;rules file\u0026rdquo; (.windsurfrules and .cursorrules respectively).\nThe whole point of using AI is to have less work you need to do. Maintaining a whole set of instructions is quite the opposite. Implementing the idea of an RFC proposal of Sasha Levin regarding the usage of AI coding assistants in the Linux Kernel project mitigates this overhead:\nTip Adding unified configuration files fore the various AI coding assistants that are all symlinked to one general AI instructions file.\nStructure of AI Assistant Instructions My experience has been that it is best to structure the instructions file. You need to give information about the codebase, which is relevant, such as the tech stack, key software components and the basic business logic workflow. A second section with development instructions such as \u0026ldquo;follow coding standards and best practices\u0026rdquo; or \u0026ldquo;use functional expressions in PHP and Javascript\u0026rdquo; is followed by general AI instructions like \u0026ldquo;If you write any code mention yourself within the commit in the format: \u0026ldquo;Co-developed-by: {Provider} {Model}\u0026rdquo; Example: \u0026lsquo;Co-developed-by: Claude claude-4-sonnet\u0026rsquo;\u0026rdquo;.\nAll instructions depend on the tech stack and the software you use. The following folder structure and exemplary instructions file are based on this Symfony skeleton repository for showcase purposes.\nRepository Structure Exemplary AI Instructions Below pdf shows what instructions might look like for a Symfony project that uses docker and just.\nUnable to display PDF file. Download PDF instead.\nView PDF (opens in new tab) ","date":"2025-08-13","permalink":"/ai-instructions-in-code-repositories/","summary":"TL;DR Place general ai instructions in your repository, for example at src/docs/ai/ai-instructions.md Specific instructions per coding assitant are symlinks to your general ai-instructions.md AI Coding Assistants? In recent years, generative artificial intelligence has become a part of everyday life. Apart from text, image and video generation, there also exist AI coding agents. The \u0026ldquo;I\u0026rdquo; for \u0026ldquo;intelligence\u0026rdquo; in AI is slightly misleading in that context. You need to give coding assistants precise instructions so they carry out tasks the way you want them to.\n","title":"AI Instructions in Code Repositories"},{"content":"Understanding web traffic is critical for assessing server performance and identifying usage patterns. GoAccess is a free, open-source tool you can use to process Nginx logs to generate structured HTML reports and gain basic insights for your website.\nTL;DR Install goaccess Use zcat to consume nginx access.log files that were compressed by logrotate and pipe them to the goaccess command: zcat /var/log/nginx/access.log.*.gz | goaccess - --log-format=COMBINED -o /your/output/dir/index.html create a systemd service and timer file to execute the command Use nginx with http-auth to make the generated report available for viewing in the browser Automated GoAccess Reports - Step-by-Step Quick-Guide Install goaccess (Ubuntu): sudo apt install goaccess Tryout the command manually: zcat /var/log/nginx/access.log.*.gz | goaccess - --log-format=COMBINED -o /your/output/dir/index.html Create systemd service file: touch /etc/systemd/system/goaccessreport.service goaccesreport.service content:\n[Unit] Description=GoAccess report generation [Service] Type=oneshot ExecStart=/usr/bin/bash -c \u0026#39;zcat /var/log/nginx/access.log*.gz | goaccess - --no-progress --log-format=COMBINED -o /your/output/dir/index.html\u0026#39; User=www-data Group=www-data [Install] WantedBy=multi-user.target Make sure /your/output/dir is owned by the user www-data (because we run the service as user www-data) touch /etc/systemd/system/goaccessreport.timer goaccessreport.timer content:\n[Unit] Description=Run GoAccess service daily at 8 AM [Timer] OnCalendar=08:00 Persistent=true [Install] WantedBy=timers.target Reload the daemon: sudo systemctl daemon-reload Enable the timer: sudo systemctl enable goaccessreport.timer Start the timer: sudo systemctl start goaccessreport.timer Check if you see your timer: systemctl list-timers Tryout your service manually: sudo systemctl start goaccessreport Configure nginx with http-auth so you can view your report: location /statistics { alias /your/output/dir; auth_basic \u0026#34;Admin Area\u0026#34;; auth_basic_user_file /etc/nginx/.htpasswd; } Systemd Service and Timer Systemd is an init system and service manager used in many Linux distributions to manage system processes. It provides tools for starting, stopping, and automating services efficiently. You can think of systemd timers as the modern way to schedule a job.\nIf you run systemctl timers-list you will see timers already defined by the system you are running on. For each .timer file (mostly located at /etc/systemd/system) a matching .service file exists (see also: https://wiki.archlinux.org/title/Systemd/Timers).\nA Systemd service is a unit configuration file that defines, how a process should be executed and managed by Systemd. In our case, the service is responsible for running GoAccess to process Nginx logs and generate an HTML report.\nUnderstanding goaccessreport.service:\nSection Directive Description [Unit] Description=... Provides a short description for the service. [Service] Type=oneshot Specifies that the service runs once and exits, rather than staying active like a typical daemon. ExecStart=... Defines the command to execute when the service starts. It decompresses Nginx log files using zcat, passes them to GoAccess for processing, and saves the output as an HTML report. User=www-data Runs the service as the www-data user (commonly used for web services), ensuring appropriate permissions. Group=www-data Ensures the process runs within the www-data group for security and access control. [Install] WantedBy=multi-user.target Ties the service to the multi-user.target, allowing it to be started manually or by other system dependencies. Understanding our goaccessreport.timer:\nSection Directive Description [Unit] Description=... Provides a short description for the timer. [Timer] OnCalendar=08:00 Defines the schedule using calendar syntax, meaning the timer triggers the service every day at 8 AM. Alternative formats allow for different scheduling patterns (e.g., weekly, hourly, or specific dates). Persistent=true Ensures that if the system was down at the scheduled time, the service will run as soon as possible after the system comes back online. This prevents the task from being skipped due to reboots or downtime. [Install] WantedBy=timers.target Ensures the timer is enabled and managed under timers.target, which is systemd’s mechanism for handling scheduled tasks. Nginx with http-auth If you are new to Nginx, check out the official beginners guide or this explanatory YT video by TechWorld with Nana. In our use case, Nginx is configured to make the GoAccess reports available via a web interface.\nTo restrict access, HTTP basic authentication is enabled, requiring users to enter a username and password before viewing the reports. This ensures that sensitive traffic data remains protected while still allowing authorized users to analyze access logs conveniently through their browser.\nSetting up http-auth: Generate a .htpasswd file You can generate the password-file on your server, but you can also generate it locally and upload it. The command needed to generate it is called \u0026ldquo;htpasswd\u0026rdquo;. Depending on your OS, it might be included in a different package. For me (using Arch Linux) I had to install \u0026ldquo;apache\u0026rdquo;. The official nginx documentation states to use \u0026ldquo;apache2-utils\u0026rdquo; for Ubuntu and \u0026ldquo;httpd-tools\u0026rdquo; when running on RHEL, CentOS or Oracle Linux.\nAfter you installed the package providing the util, you can run htpasswd -c /your/out/dir/.htpasswd your_user. You will then be asked to enter the password. In the resulting file, you will see an entry like: \u0026ldquo;your_user:hashed-password-you-just-entered\u0026rdquo;.\nCopy that file to your server. I chose the location /etc/nginx because the file will be used by nginx so that seemed to be a fitting location.\nConfigure nginx to use the .htpasswd-file location /statistics { alias /your/output/dir; auth_basic \u0026#34;Admin Area\u0026#34;; auth_basic_user_file /etc/nginx/.htpasswd; } After changing the nginx configuration, you have to reload the service so that changes take effect: sudo systemctl reload nginx\nAnd with that being done when you access your-blog.domain/statistics you should be asked for the user and password you defined in the .htpasswd file and only be shown the GoAccess HTML report after entering a valid user-password combination.\nGoAccess HTML-Report - what insights can I gain? The report generated by goaccess provides you with basic yet useful insights, such as:\ncrawler requests -\u0026gt; if crawlers cause a lot of load you can identify the most aggressive ones and take actions to block them 404 URLs -\u0026gt; if you migrated from another technology, like I did, you might want to check that you did not forget some redirects time distribution -\u0026gt; analyzing requests over time can give you different valuable insights. Maybe you addressed issues uncovered by the Google Search Console or improved performance and can pinpoint a change in the request volume. Maybe you were the first to write on trending topics and can deduce an increase or decrease in traffic based on when you publish articles and promote them (i.e. on social media) ","date":"2025-05-31","permalink":"/goaccess-getting-started/","summary":"Understanding web traffic is critical for assessing server performance and identifying usage patterns. GoAccess is a free, open-source tool you can use to process Nginx logs to generate structured HTML reports and gain basic insights for your website.\nTL;DR Install goaccess Use zcat to consume nginx access.log files that were compressed by logrotate and pipe them to the goaccess command: zcat /var/log/nginx/access.log.*.gz | goaccess - --log-format=COMBINED -o /your/output/dir/index.html create a systemd service and timer file to execute the command Use nginx with http-auth to make the generated report available for viewing in the browser Automated GoAccess Reports - Step-by-Step Quick-Guide Install goaccess (Ubuntu): sudo apt install goaccess Tryout the command manually: zcat /var/log/nginx/access.log.*.gz | goaccess - --log-format=COMBINED -o /your/output/dir/index.html Create systemd service file: touch /etc/systemd/system/goaccessreport.service goaccesreport.service content:\n","title":"Getting Started with GoAccess: Visualizing Nginx Access Logs as HTML Reports"},{"content":"TL;DR Provide a zipped version of resources on your server: gzip -k --best inputFileName Configure nginx to use gzip compression # /etc/nginx/nginx.conf http { ... ## # Gzip Settings ## gzip on; gzip_static on; gzip_vary on; gzip_comp_level 6; gzip_types application/pdf text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript images/svg+xml; ... } What is Gzip? Gzip is an application that can be used to compress files. By applying an algorithm, the file size is reduced as an outcome of running gzip. The compressed file has the extension \u0026ldquo;.gz\u0026rdquo;.\nHow does serving zipped files even work? When a client (like a web browser) makes a request, it sends info on what content types it can process in the \u0026ldquo;Accept-Encoding\u0026rdquo; header. Most modern browsers are able to process gzip. You can find out/observe this when opening the network-tab in the browser\u0026rsquo;s DevTools. After loading a page, you can click on a resource and see the request headers. When a browser makes a request to - for example - your website, it requests resources on your server. Even for a website like a blog, small things may add up. Implementing gzip is a straightforward yet impactful method to significantly enhance page load times. If you load a website you can see two columns in the DevTool\u0026rsquo;s network tab: \u0026ldquo;transferred\u0026rdquo; and \u0026ldquo;size\u0026rdquo;.\nThe values for \u0026ldquo;transferred\u0026rdquo; and \u0026ldquo;size\u0026rdquo; differ because the browser received a response with \u0026ldquo;Content-Encoding\u0026rdquo;: gzip. The browser decompresses the zipped resource and thus can display the amount of data transferred and the actual size of the resource. In the above example, compression with gzip reduced the size of resources to approx. one-third of their original size.\nCompress files with Gzip The gzip command can be configured with a lot of options. You can use gzip --help for further reference. For my blog I chose the simple approach:\ngzip -k --best inputFileName The argument -k tells gzip to keep the original. I want to keep it because I want to be able to respond to requests for my blog with the uncompressed resource, if the client does not accept gzip. --best is long for -9 which means to use the best compression level available, so my compressed files are as small as possible.\nNginx configuration for Gzip Nginx is capable of serving .gz files per default. All you have to do is enable and configure it. In your nginx.conf (mostly located at /etc/nginx/nginx.conf) you have to insert (or uncomment) the following lines:\ngzip on; gzip_static on; gzip_vary on; gzip_comp_level 6; gzip_types application/pdf text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript images/svg+xml; When using gzip compression with nginx, there are two things to understand:\nnginx can compress resources \u0026ldquo;on the fly\u0026rdquo; You can configure nginx to use the compressed resource you provided, so you do not compress anew at every request What does above configuration do?\nDirective Description gzip on; Enables gzip compression for responses to reduce file size and improve load times. gzip_static on; Allows nginx to serve pre-compressed files (e.g., .gz files) if they exist, bypassing the need for on-the-fly compression. gzip_vary on; Sends the Vary: Accept-Encoding header to inform proxies and browsers that different versions of the resource exist. gzip_comp_level 6; Sets the compression level to 6 (a balance between compression efficiency and CPU usage; values range from 1 to 9). gzip_types application/pdf text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript images/svg+xml; Specifies the MIME types for files that should be compressed by gzip. This includes files like PDFs, plain text, CSS, JSON, JavaScript, XML, and SVGs. ","date":"2025-04-18","permalink":"/gzip-reduce-website-load-times/","summary":"TL;DR Provide a zipped version of resources on your server: gzip -k --best inputFileName Configure nginx to use gzip compression # /etc/nginx/nginx.conf http { ... ## # Gzip Settings ## gzip on; gzip_static on; gzip_vary on; gzip_comp_level 6; gzip_types application/pdf text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript images/svg+xml; ... } What is Gzip? Gzip is an application that can be used to compress files. By applying an algorithm, the file size is reduced as an outcome of running gzip. The compressed file has the extension \u0026ldquo;.gz\u0026rdquo;.\n","title":"Gzip: Reduce Website Load Times"},{"content":"If you are new to Marp, check out this article that covers the basics: Marp: Create Presentations using Markdown\nTL;DR you need to use a theme stylsheet (theme.css) you can write regular css like you know it use img[alt~=\u0026quot;your-alias\u0026quot;] {...css} to be able to use your-alias to apply the styling on images the same as you use - for example - the image filters provided by Marp out of the box Align images left or right CSS:\nimg[alt~=\u0026#34;align-right\u0026#34;] { float: right; } img[alt~=\u0026#34;align-left\u0026#34;] { float: left; } Usage:\n![w:500 align-left](image.png) ![w:500 align-right](image.png) Display images side by side CSS:\n/* use on containing element to display images side by side */ .inline-images { display: flex; height: 80%; /* control location on y-axis */ justify-content: space-evenly; align-items: center; } Usage:\n\u0026lt;div class=\u0026#34;inline-images\u0026#34;\u0026gt; \u0026lt;div\u0026gt; ![w:500](image.png) \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; ![w:500](image.png) \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Align images to the top and bottom of a slide CSS:\nimg[alt~=\u0026#34;align-bottom-centered\u0026#34;] { position: absolute; bottom: 0; left: 50%; /* centering */ transform: translateX(-50%); /* centering */ margin: 0; /* control space to bottom end of slide */ } img[alt~=\u0026#34;align-top-centered\u0026#34;] { position: absolute; top: 0; left: 50%; /* centering */ transform: translateX(-50%); /* centering */ margin: 0; /* control space to top end of slide */ } Usage:\n![w:500 align-top-centered](image.png) ![w:500 align-bottom-centered](image.png) Working Example Check out this exemplary repository to see the styles listed above in action: https://github.com/m1rm/marp-skeleton\n","date":"2025-03-28","permalink":"/marp-advanced-image-positioning/","summary":"If you are new to Marp, check out this article that covers the basics: Marp: Create Presentations using Markdown\nTL;DR you need to use a theme stylsheet (theme.css) you can write regular css like you know it use img[alt~=\u0026quot;your-alias\u0026quot;] {...css} to be able to use your-alias to apply the styling on images the same as you use - for example - the image filters provided by Marp out of the box Align images left or right CSS:\n","title":"Marp: Advanced Image Positioning"},{"content":"TL;DR mkdir content/series inside the series folder, create subfolders: \u0026lt;insertSeriesName\u0026gt;/_index.md control title and description of the series overview page via the _index.md front-matter add series = 'series under [taxonomies] in the config.toml use series = ['insertSeriesName'] in an article front-matter to make it part of a series if you want to display that an article is part of a series, there are different ways: you can do so manually per article with a link you can alter the template for single posts (see below) you can use a shortcode Series Code Snippet Code location: layouts/_default/single.html\nThe snippet has to be inserted in your existing template. If you use a third party theme, you have to go to the theme\u0026rsquo;s GitHub, download the theme template and alter it at a position you see fit. The template from your project will be used during build, not the theme one.\nInfo Keep in mind when doing such alterations to a third party theme, that you might not be able to do theme updates blindly anymore. This depends on the changes you made.\n{{ if .Param \u0026#34;series\u0026#34; }} {{ $currentPage := .Page.Permalink }} {{ $name := index .Params.series 0 }} \u0026lt;p style=\u0026#34;margin-bottom: 16px;\u0026#34;\u0026gt; This article is part of the series \u0026#34;\u0026lt;a href=\u0026#34;/series/{{$name | urlize }}\u0026#34; \u0026gt;{{$name}}\u0026lt;/a \u0026gt;.\u0026#34; \u0026lt;/p\u0026gt; {{end}} ","date":"2025-03-15","permalink":"/hugo-create-post-series/","summary":"TL;DR mkdir content/series inside the series folder, create subfolders: \u0026lt;insertSeriesName\u0026gt;/_index.md control title and description of the series overview page via the _index.md front-matter add series = 'series under [taxonomies] in the config.toml use series = ['insertSeriesName'] in an article front-matter to make it part of a series if you want to display that an article is part of a series, there are different ways: you can do so manually per article with a link you can alter the template for single posts (see below) you can use a shortcode Series Code Snippet Code location: layouts/_default/single.html\n","title":"Hugo: Create a Post Series"},{"content":"TL;DR https://gohugo.io/content-management/urls/ there are several ways to control routing default: file path within the content directory front matter slug overwrites last segment of path front matter url overwrites the whole path permalink settings front matter url overwrites any matching permalink pattern My configuration Above screenshot shows the structure of the code that powers this blog. I chose to have a directory structure that fits my way of organizing/thinking. In consequence, I also chose to use the url in the frontmatter to control the full routes explicitly per article.\nThe article in content/post/hello-world/index.md has url = 'hello-world/' in the front matter so the post will be available under \u0026ldquo;https://miriam-mueller.com/hello-world\u0026quot;. Within the rules Hugo applies, you are free to do what suits you best. You could achieve the same result with this structure: content/hello-world.md, for example. The benefit I saw in my structure is, that I can place all assets used in the post in one folder with its markdown.\nMigrate WordPress routes Firstly, you need to know the routes you have. This sounds more simple than it is, because WordPress generates a lot of stuff you might not even know about. How to find out your routes? You can generate an export of your WordPress site in xml format and look them up. I had my site indexed at Google and with a Google Search Console account you can quickly access your \u0026ldquo;most important sites\u0026rdquo; from the point of view if they were frequently accessed because of being a search result and ranking well.\nDecide what to keep Migrating the articles is quite simple. My scheme was baseURL/article-title/ and I decided to keep the routes as they were for already published articles. For asset-URLs (images) I chose to not include the old routes in my nginx-configuration. The same accounts for css/js of my WordPress theme. This means, if - for example - someone bookmarked the asset URL, they will now end up in my new blog with the default 404 page.\nFor routes that you want to redirect into your new blog, you need to set up nginx rewrite rules. In my case, this accounted for the Marp Cheatsheet and routes for categories and tags because I wildly used them in my old blog. If you - like me - decide to use the opportunity to clean up, you will end up with a lot of redirects, but it is worth the effort.\nExemplary nginx redirects location = /wp-content/uploads/2024/05/MarpCheatsheet.pdf { return 301 /MarpCheatsheet.pdf; } location ~* ^/2024/?$ { return 301 \u0026#34;/archives/#2024\u0026#34;; } location ~* ^/category/symfony/?$ { return 301 /tags/symfony/; } Hugo taxnonomy configuration In order for the tags-routes and archive pages to work, you need to configure the taxonomies in the config.toml:\n[taxonomies] year = \u0026#39;year\u0026#39; tags = \u0026#39;tags\u0026#39; For the archive page to work you have to define the year in the front-matter of each article, as well as the date. The rest will be generated automatically by Hugo.\n+++ date = \u0026#39;2025-03-15T17:00:00+01:00\u0026#39; draft = false title = \u0026#39;Hugo directory structure \u0026amp; routing\u0026#39; tags = [\u0026#39;hugo\u0026#39;, \u0026#39;wordpress\u0026#39;] year = 2025 url = \u0026#39;hugo-directory-structure-and-routing/\u0026#39; +++ If you want to influence a tag page, you can add content/tags/\u0026lt;insertTag\u0026gt;/_index.md. For example in the front-matter you can set a page title and description. The /tags route will be automatically generated by hugo once you use tags.\n","date":"2025-03-15","permalink":"/hugo-directory-structure-and-routing/","summary":"TL;DR https://gohugo.io/content-management/urls/ there are several ways to control routing default: file path within the content directory front matter slug overwrites last segment of path front matter url overwrites the whole path permalink settings front matter url overwrites any matching permalink pattern My configuration Above screenshot shows the structure of the code that powers this blog. I chose to have a directory structure that fits my way of organizing/thinking. In consequence, I also chose to use the url in the frontmatter to control the full routes explicitly per article.\n","title":"Hugo directory structure \u0026 routing"},{"content":"TL;DR read the official quick start guide instead of git submodule, use go modules to require your theme (state of the art) working themes for tryout are: stack, paperMod Why Hugo open source fast static site generator ready to use support for taxonomies a variety of free themes exist simple \u0026amp; clear project structure easy to customize embedded webserver for easy development Project setup https://github.com/m1rm/hugo-skeleton\ninstall hugo on your system use hugo new site hugoTest to get started cd into the newly created repo and run git init add your upstream repository (i.e. a repository at GitHub in the following steps) run hugo mod init github.com/yourUser/yourUpstreamRepository to initialize your go module to keep track of dependencies such as your Hugo theme create hugoTest\\config\\_default\\config.toml create hugoTest\\config\\_default\\module.toml create content/post/my-first-post/index.md config.toml content\nlanguageCode = \u0026#39;en-us\u0026#39; title = \u0026#39;Hugo Skeleton\u0026#39; [outputs] home = [\u0026#34;HTML\u0026#34;] module.toml content\n[[imports]] path = \u0026#34;github.com/adityatelange/hugo-PaperMod\u0026#34; my-first-post/index.md content\n+++ draft = true title = \u0026#39;Setting up a Hugo project\u0026#39; +++ # Hugo Hello! run hugo mod tidy and observe the changes in your go.mod file. You will see the theme dependency. run hugo server --buildDrafts --ignoreCache --noHTTPCache --cleanDestinationDir to start the embedded web server and navigate to localhost:1313 to see your first site in action. Hugo places the generated sites in the public directory. The content of this directory is also the content you want to sync to your server inside /var/www/html (if you have been following the previous article of this series) or to your webserver root from where you want to host your blog.\n","date":"2025-03-15","permalink":"/hugo-getting-started/","summary":"TL;DR read the official quick start guide instead of git submodule, use go modules to require your theme (state of the art) working themes for tryout are: stack, paperMod Why Hugo open source fast static site generator ready to use support for taxonomies a variety of free themes exist simple \u0026amp; clear project structure easy to customize embedded webserver for easy development Project setup https://github.com/m1rm/hugo-skeleton\n","title":"Set up a new Hugo project"},{"content":"TL;DR Server Setup\nchose a provider, create (and verify) your account rent a virtual server fitting your needs connect to your server as root add a new user to your server that is in the sudoers group verify that you can connect as the user and use sudo disable password and root login setup ssh to connect as your user automatically Webserver Setup\ninstall \u0026amp; setup nginx let your user own nginx root directory check for logrotate setup letsencrypt let your domain point to your new server Chosing a provider There are different providers on the market. Famous, big names might be Amazon Web Services (AWS) or GCP (Google Cloud Platform). You can also self-host a blog using GitHub Pages. Depending on what you want to pay and what features you want to have, your choice can differ from mine. The main criteria I considered where:\ncompany \u0026amp; company principal office bc. of legal reasons pricing My choice I chose Hetzner because I had experience with it already from my jobs and friends who had been customers there already. After registering an account, you can add your payment information and book a server. The provisioning of the virtual server with Ubuntu took less than three minutes. At Hetzner, you can add an SSH-key before the server is provisioned. Thus, you can directly ssh onto your server once the provisioning is done using ssh root@yourServerIP.\nServer setup - Ubuntu When connecting the first time, you will connect as root. Root logins are a target of attacks and the root user is the user that can do - and break - everything. It is a security best-practice to not connect as root. Moreover, it is also recommended to disable password login. Thus, you want to create your own \u0026ldquo;admin\u0026rdquo; user, that is part of the sudoers group so you can do things only root can do (using sudo) while disabling password login and root login to protect against attacks (and password leaks).\nUser Setup\nAs root, run adduser yourusername to add your user As root, run usermod -aG sudo yourusername to add your user to the sudoers group As root, set a root password with passwd (you need that when switching to the user on the server) As root, set a password for your user: passwd username (you need that when using sudo with your user) Copy your public ssh-key to the server for your user so your user can connect via ssh: ssh-copy-id yourusername@yourServerIP (it adds your public key to the .ssh/authorized_keys of the user) Security Setup Warning Keep the terminal with the root user connection open so you still have a root connection if something goes wrong during the setup.\nAs root, edit /etc/ssh/sshd_config: Set PasswordAuthentication no to disable password login Set PermitRootLogin no to disable root login Resart the ssh-service for changes to take effect: systemctl restart ssh Start a new terminal and try to connect as root. It should fail. Try to connect as your user ssh yourusername@yourServerIP. It should work. Switch to the root user using su root. Enter the root password. It should work. Use exit to switch back. Local Setup\nFor convenience, you can modify your local ssh-config so that you automatically connect as your user to your server. Instead of ssh yourusername@yourServerIP you then only need to enter ssh yourServerIp. Once you have DNS enabled and your domain pointing to your server IP, this setting can be used to connect like ssh your-domain.com. Edit your .ssh/config (create the file if it does not exist):\nHost your-domain.com User yourusername Webserver setup Nginx Your virtual server is blank. You will need a Webserver to answer incoming requests. Install nginx running sudo apt install nginx. Nginx comes with some default config. Checkout /etc/nginx on your server to make yourself familiar with it. For details, checkout the official nginx guide. In /etc/nginx/nginx.conf. it is those two lines that matter (in there per default after the installation):\ninclude /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; In sites-enabled, you will see a default symlink to a config in sites-available. I followed this default and renamed the config inside sites-available to \u0026ldquo;blog.conf\u0026rdquo; (because it is for my blog). All configuration for routing requests to my blog goes in there.\nWithout changes, the default config inside sites-available should show a block like this:\nlocation / { root /var/www/html; index index.html; try_files $uri $uri/ =404; } So the document root that is used by nginx to look for files to serve is /var/www/html. Because my blog consists only of static files, I will put the content in that very directory. In order to keep my security setup, I want that directory to be owned by the user I use to connect to my server. I will use that very user to sync files to the server.\nChange ownership of /var/www/html sudo chown $USER:yourusername /var/www/html\nSee \u0026ldquo;your\u0026rdquo; first site If you enter your IP in a browser, you should see the \u0026ldquo;Welcome to Nginx\u0026rdquo; landingpage. Congratulations, you just accessed a resource on your server!\nLogrotate Nginx writes logs. You can see them in /var/log/nginx. Those logs will slowly consume storage on your server, if you do not do something about it. There is a ready to use solution called logrotate. DigitalOcean has a neat article on setting up logrotate on Ubuntu. With my machine on Hetzner, logrotate was preconfigured and all I had to do was check the log retention times.\nSSL \u0026amp; DNS Warning Before carrying out this step, you might want to migrate content of your blog to /var/www/html before you switch to serving content for your-domain.com from the new server we just set up.\nThe first site we saw earlier was accessible via http only. State of the art is using https. In order to be able to serve your content via https, you need a valid SSL certificate. There is a free service you can set up on your server to take care of that: letsencrypt.\nDigitalOcean got us covered again, I followed this guide to set up letsencrypt with nginx on Ubuntu.\nInfo The important note here is: you need to have your domain registered already.\nI had one because this blog existed before I migrated to an own virtual server. I had my domain registered via IONOS. So I had to log in to my IONOS Admin backend and change the A and AAAA records for my domain to point to my new server\u0026rsquo;s IP. As soon as you do that, your blog is down. In order for certbot to work, you need to have your domain pointing to your IP before you run certbot. Otherwise, certbot cannot verify the domain ownership and will fail.\nIf it succeeds, you should be able to reach your site via https and your blog is online again, now served from your new server.\n","date":"2025-03-09","permalink":"/self-hosting-get-started/","summary":"TL;DR Server Setup\nchose a provider, create (and verify) your account rent a virtual server fitting your needs connect to your server as root add a new user to your server that is in the sudoers group verify that you can connect as the user and use sudo disable password and root login setup ssh to connect as your user automatically Webserver Setup\ninstall \u0026amp; setup nginx let your user own nginx root directory check for logrotate setup letsencrypt let your domain point to your new server Chosing a provider There are different providers on the market. Famous, big names might be Amazon Web Services (AWS) or GCP (Google Cloud Platform). You can also self-host a blog using GitHub Pages. Depending on what you want to pay and what features you want to have, your choice can differ from mine. The main criteria I considered where:\n","title":"Get started with self-hosting"},{"content":"What does self-hosting mean? Self-hosting means to run and maintain your website or service using a private web server. In the context of this article series you can think of it as the opposite of using a managed hosting solution. This blog started on such a managed solution, a managed WordPress installation. This means, that I paid the provider I chose for provisioning of the service (WordPress) and all I got was an admin login to the WordPress installation. Within the installation, I was free to do what I wanted (and also responsible for it).\nIn the managed solution, I got what I bought: WordPress. Now I am renting a server and I have full control what I do on it.\nSelf-Hosting: rent a (virtual) server If you take the word \u0026ldquo;self-hosting\u0026rdquo; a 100%, it would mean you have a server standing in your flat, cellar or garage and on that server, you run your services and host your website(s). The \u0026ldquo;soft entry\u0026rdquo; into self-hosting is to rent a server. You can either rent a physical server or a virtual one. For a start, the virtual one is cheaper and definitely enough for hosting your own website.\nAdvantages and Disadvantages of self-hosting As outlined already, you have a lot more freedom. You can connect to your server and freely install anything. Want to tryout hosting a small API that gives static answers to learn about authentication? Go ahead? Migrate your blog - like I did - to Hugo. Why not, just do it.\nThe - maybe obvious disadvantage - is, that nothing is provisioned for you apart from the server and your root login. You have to take care of dependencies, basic server security and things you need on your own. What does that mean? Well, here\u0026rsquo;s one example: My WordPress blog came with e-mail alerts. My Hugo blog does not. Why? Because I did not set it up.\nSelf-hosting means more responsibilities on your side, but I think it is worth it for the learnings you will have. After all, I managed to do it, so you can, too!\n","date":"2025-03-09","permalink":"/self-hosting-pro-and-contra/","summary":"What does self-hosting mean? Self-hosting means to run and maintain your website or service using a private web server. In the context of this article series you can think of it as the opposite of using a managed hosting solution. This blog started on such a managed solution, a managed WordPress installation. This means, that I paid the provider I chose for provisioning of the service (WordPress) and all I got was an admin login to the WordPress installation. Within the installation, I was free to do what I wanted (and also responsible for it).\n","title":"Self-Hosting: Pro and Contra"},{"content":"Many laptops come with an extra GPU. In the past it was difficult to setup your computer to use the dedicated (powerful) GPU (dGPU) for heavier workloads while it can use the integrated GPU (iGPU) for anything else. Once the switcheroo service was setup, I had nothing to do anymore but let it handle my GPUs.\nPrerequisites Switcheroo is a solution for GNOME only.\nCheck which GPU is in use Use: glxinfo | grep \u0026quot;OpenGL renderer\u0026quot; to see what GPU is currently in use. The output on my machine was: OpenGL renderer string: AMD Radeon Graphics (...). Install (for example) Steam and navigate to “Help \u0026gt; System Information” to see which GPU is recognized. Without switcheroo, Steam recognized my Radeon iGPU. Start a graphics intensive game and see if it is lagging. It was in my case.\nInstall switcheroo \u0026amp; enable it Make sure beforehand that you have up-to-date GPU drivers.\nsudo pacman -Syu switcheroo-control sudo systemctl enable switcheroo-control.service Reboot systemctl status switcheroo-control.service How do I know it works? I checked it by checking the Steam settings again. Instead of my iGPU Steam recognized my dGPU. When trying out the same game again and it was lag free. When checking my system with glxinfo however, it was still the AMD Radeon being in use. Just as intended, the dGPU will be used for the heavy lifting in games only.\nHow does switcheroo work? Applications have a “desktop file”. This file specifies for example the desktop icon you see when you create a desktop shortcut for the application. It can also specify, which graphic card to use.\nSteam is using this, but see for yourself:\npacman -Qql steam | grep desktop open /usr/share/applications/steam.desktop Todo: insert image\nThis also means it might not work out of the box for any other games or other platforms.\nSources https://wiki.archlinux.org/title/NVIDIA_Optimus https://wiki.archlinux.org/title/PRIME#GnomeYou are using GNOME._integration ","date":"2024-08-18","permalink":"/switcheroo-handle-igpu-and-dgpu-under-gnome/","summary":"Many laptops come with an extra GPU. In the past it was difficult to setup your computer to use the dedicated (powerful) GPU (dGPU) for heavier workloads while it can use the integrated GPU (iGPU) for anything else. Once the switcheroo service was setup, I had nothing to do anymore but let it handle my GPUs.\nPrerequisites Switcheroo is a solution for GNOME only.\nCheck which GPU is in use Use: glxinfo | grep \u0026quot;OpenGL renderer\u0026quot; to see what GPU is currently in use. The output on my machine was: OpenGL renderer string: AMD Radeon Graphics (...). Install (for example) Steam and navigate to “Help \u0026gt; System Information” to see which GPU is recognized. Without switcheroo, Steam recognized my Radeon iGPU. Start a graphics intensive game and see if it is lagging. It was in my case.\n","title":"Switcheroo Handle iGPU and dGPU Under Gnome"},{"content":"TL;DR The following three games are my favorites to learn and/or improve SQL skills:\nhttps://selectstarsql.com/ https://sqlpd.com/ https://mystery.knightlab.com/ Select Star SQL Free: ✅ Installation required: ⛔ Skill level: beginner Time to complete: About 10-15 minutes per chapter An interactive book to learn SQL from the very beginning. It is organized in chapters with interactive sections where you first run queries, then alter them and finally write your own ones.\nSQL Police Free: 50/50 Installation required: ⛔ Skill level: beginner In this game you are a data analyst preparing data. Instead of typing by yourself, you select statements, table names and other operators via the GUI. The game is not fully free, but there are free examples and a full SQL guide under the \u0026ldquo;guide\u0026rdquo; tab.\nSQL Murder Mystery Free: ✅ Installation required: ⛔ Skill level: medium After an introduction you can start querying the database to find out who committed a murder. The game is fully browser based (see Fig. 2). If you want a challenge, try to solve the game in as few queries as possible.\n","date":"2024-07-15","permalink":"/fun-ways-to-learn-sql/","summary":"TL;DR The following three games are my favorites to learn and/or improve SQL skills:\nhttps://selectstarsql.com/ https://sqlpd.com/ https://mystery.knightlab.com/ Select Star SQL Free: ✅ Installation required: ⛔ Skill level: beginner Time to complete: About 10-15 minutes per chapter An interactive book to learn SQL from the very beginning. It is organized in chapters with interactive sections where you first run queries, then alter them and finally write your own ones.\n","title":"Fun Ways to Learn Sql"},{"content":"What is Google I/O? Google I/O is a conference hosted by and about Google. While the main conference is taking place in the US in San Francisco, Google started to host the Google I/O Connect conferences in 2023. Those are taking place in multiple places in the world and have the same setup and topics as the main conference, scaled down a bit.\nI/O Connect Berlin The agenda is accessible beforehand, so you can plan your day. Just like the main conference, there are four core topics this year:\nAI Web Mobile Cloud The meeting was opened by Tim Messerschmidt giving some impressive numbers. 1300 people from over 100+ EMEA countries were present in Berlin. Afterwards, various speakers gave a quick overview of the most important topics and innovations, especially in the area of AI. The talks after the opening remarks are taking place in parallel, so you have to make a selection. I visited the AI and the Web stage.\nKey Takeaways AI The topics covered fit into the bigger picture of AI changing the way we will develop in the future, but also show that there is quite some way to go. While Google advertises its cloud services and the power of their Gemini models, the Chrome team points out that a model running on the device has the benefit of of lower cost for both provider and end user (regarding money and loading times/latency) while offering the possibility of GDPR conformity. Because it runs on the device only without sending data anywhere. Google has a Gemini model for it, Gemini Nano, which is also integrated into Chrome and the Chrome Dev Tools.\nSeveral presentations showed, that AI will be integrated into more and more products as a supportive agent rather than “replacing developers”. VScode, IntelliJ and others will see Gemini integration, Google Cloud Run will use AI to support you in simple Dev Ops tasks to name a few. While announcing a lot of AI integrations and products utilizing AI, you can also see a trend in resource usage awareness. Not for environmental, but for monetary reasons. Gemini Flash and Pro are extended with context caching – parts of the prompts that do not change can be cached to save money. Gemma 2 was announced with the key features of being more efficient and safe and needing only one single GPU.\nFor the use of AI models, one proposal is a hybrid approach. Download the model and run on device if the device can do it, otherwise run on the server. An intriguing thought but at the time of writing, the vast majority of mobile devices is not able to run such models at all or reasonably fast enough to provide a real benefit to the end user. But hardware development has already taken the steps so that future devices will be capabable of doing so.\nAI’s hunger for resources also has effects beyond the virtual world. It is not hot news, but Google is investing a lot in cloud infrastructure in Europe and Africa as well as even building the first ever under sea fiber-optic cable to connect Africa and Australia.\nWeb “AI is only as good as the experience built around them“. This quote from one of today’s sessions at the web stage, combined with “focus on the user and all else will follow” from Google’s “ten things we know to be true” are the frame for some of the key takeaways from the talks. During the last year, many new features have arrived in the browsers to provide your users with a better experience. But: many of the announced features are not yet implemented by Firefox and Safari, therefore you should treat this information with attention but not overhype it.\nThe Speculation Rules API lets you prerender whole pages by defining a json of “speculationrules” in a script tag. The View Transition API aims at providing seamless viusal transitions between different views. Combined with speculation rules, navigation can then feel “almost instant”. One of the more established new APIs is the Document Picture-in-Picture API, which powers the little floating player of Spotify. Last but not least, the developer documentation on scroll-driven animations is worth a read for anyone interested in a good UX when scrolling down a page – a thing that happens quite often in the vertical world of the mobile web.\nAll the new APIs and (incoming) native support for features do not only aid the developer experience but also the end user, because less lines of CSS/JS are needed and thus the load for shipping good UX is decreased. Yet, not all browsers and (naturally) older versions do implement those features. With the recent events around polyfill.io, one has to wonder how to safely implement the new features. A simple, yet effective advice to deal with this was given by the Google experts: host polyfills yourself and check beforehand what you host. I know many developers who simply use polyfills without thinking about it. If it is worth the effort you can also consider checking, if the use of a new API and the polyfill outperforms self written code.\nFollow Up A lot more was presented at I/O Connect, and it is too much to cover in one blog post. Use below list to dive into specific topics:\nGoogle I/O – Content on Demand Google Gemini Models Google Gemma Family Chrome for Developers Chrome for Developers – Web Platform Prerender Pages (Speculation Rules API) View Transitions API Picture-in-Picture API Webmanifest v3 transition Web Assembly (WASM) ","date":"2024-06-27","permalink":"/google-i-o-connect-2024-impressions/","summary":"What is Google I/O? Google I/O is a conference hosted by and about Google. While the main conference is taking place in the US in San Francisco, Google started to host the Google I/O Connect conferences in 2023. Those are taking place in multiple places in the world and have the same setup and topics as the main conference, scaled down a bit.\nI/O Connect Berlin The agenda is accessible beforehand, so you can plan your day. Just like the main conference, there are four core topics this year:\n","title":"Google I/O Connect 2024 Impressions"},{"content":"Marp is a tooling to create presentations based on markdown and css.\nTL;DR checkout this post for the basics of Marp: Marp: Create Presentations using Markdown Install the Marp for VSCode extension create yourtheme.css and register it in yourpresentation.md via a global directive create a .vscode/settings.json and register your theme there want to build your presentations via CLI? Check out my GitHub repository VSCode settings First, search for \u0026ldquo;Marp\u0026rdquo; in the extensions tab and install Marp for VSCode from marp-team\nPreview Button With the preview button you can have a side by side view of your markdown and the presentation slides. The changes you make in your markdown are shown in the slides in real-time.\nGenerate the presentation manually With the \u0026ldquo;Marp Command Button\u0026rdquo; you can enter an interactive save dialog to export your presentation.\nUsing style sheets If you want to customize the presentation via a style sheet, you need to be aware on how to register your custom theme via the plugin and how to use it in your presentation markdown file.\nCreate your theme.css and declare the theme name in the first line: /* @theme yourthemename */\nConfigure which theme your presentation.md should use via the global directive:\n--- marp: true theme: yourthemename --- Registering your style sheets Open the Settings (File -\u0026gt; Preferences -\u0026gt; Settings) and search for \u0026ldquo;marp\u0026rdquo; to display the Marp plugin settings Under \u0026ldquo;Markdown \u0026gt; Marp: Themes\u0026rdquo; you need to register your themes to make them available by adding the style sheet\u0026rsquo;s name. Alternative to adjusting settings by hand In your Marp project, you can check in a .vscode folder with a settings.json. In there, you can register your themes:\n{ \u0026#34;markdown.marp.themes\u0026#34;: [ \u0026#34;relative/path/to/your/theme1.css\u0026#34;, \u0026#34;relative/path/to/your/theme2.css\u0026#34; ] } How do I know my theme works? If your theme is not registered correctly, VSCode will show a hint in the presentation.md file like \u0026ldquo;the specified theme is not recognized by Marp for VSCode\u0026rdquo;. If your theme is registered, you will see changes take effect immediately in the markdown preview.\n","date":"2024-06-16","permalink":"/marp-vscode-setup/","summary":"Marp is a tooling to create presentations based on markdown and css.\nTL;DR checkout this post for the basics of Marp: Marp: Create Presentations using Markdown Install the Marp for VSCode extension create yourtheme.css and register it in yourpresentation.md via a global directive create a .vscode/settings.json and register your theme there want to build your presentations via CLI? Check out my GitHub repository VSCode settings First, search for \u0026ldquo;Marp\u0026rdquo; in the extensions tab and install Marp for VSCode from marp-team\n","title":"Marp Vscode Setup"},{"content":"If you are passionate about open source software and looking for a project to contribute to, you might want to give the Arch Linux pkgstats project a try.\nWhat is pkgstats? Pkgstats aims at collecting valubale information about installed packages while respecting user privacy. Data is gathered anonymously, which provides insights that benefit the entire community.\nHow does it work? If you are an Arch Linux user, you can install the pkgstats package via pacman -Syu pkgstats. Pkgstats then quietly collects relevant data, such as installed packages, used mirrors or the system architecture. No personal information is ever recorded. So, as a user you can contribute by simply having the package installed.\nContribute to pkgstats.archlinux.de Insights gained are made available on the pkgstats website: https://pkgstats.archlinux.de/. These insights help developers, maintainers and the Arch Linux community make informed decisions. The website code is publicly available at Github. Pkgstats offers a “fun section” where you can easily get involved, but all other issues are worth a look as well!\nLooking for more? Be sure to pay Pierre Schmitz’ blog a visit (he is the author of pkgstats): https://pierre-schmitz.com/.\nIf you want to contribute to another project, check out https://whatcanidofor.archlinux.org/.\nHappy Coding \\m/\n","date":"2024-06-06","permalink":"/arch-linux-pkgstats-a-good-first-issue/","summary":"If you are passionate about open source software and looking for a project to contribute to, you might want to give the Arch Linux pkgstats project a try.\nWhat is pkgstats? Pkgstats aims at collecting valubale information about installed packages while respecting user privacy. Data is gathered anonymously, which provides insights that benefit the entire community.\nHow does it work? If you are an Arch Linux user, you can install the pkgstats package via pacman -Syu pkgstats. Pkgstats then quietly collects relevant data, such as installed packages, used mirrors or the system architecture. No personal information is ever recorded. So, as a user you can contribute by simply having the package installed.\n","title":"Arch Linux Pkgstats a Good First Issue"},{"content":"Marp is a tooling you can use to create PDF or html presentations based on a single markdown file. This offers the benefit of easier cooperation and versioning, since you can simply use a Github repository to hold your presentation(s) and assets. In addition, you can write plain simple css to customize the appearance of your slides.\nKey Concepts In order for a Marp project to work you need to be aware of a few key concepts. You can explore them by example in my marp-skeleton repo on Github. If you are not using VSCode with its Marp plugin, you need the marp-cli to build the presentation PDF (or html). See https://github.com/marp-team/marp-cli on how to install it on various systems.\nYou then have to enable Marp in your presentation markdown file:\n--- marp: true --- Cheatsheet If you want to leverage the actual power of Marp, below cheatsheet summarizes many of its special features.\nUnable to display PDF file. Download PDF instead.\nView PDF (opens in new tab) ","date":"2024-06-01","permalink":"/marp-create-presentations-using-markdown/","summary":"Marp is a tooling you can use to create PDF or html presentations based on a single markdown file. This offers the benefit of easier cooperation and versioning, since you can simply use a Github repository to hold your presentation(s) and assets. In addition, you can write plain simple css to customize the appearance of your slides.\nKey Concepts In order for a Marp project to work you need to be aware of a few key concepts. You can explore them by example in my marp-skeleton repo on Github. If you are not using VSCode with its Marp plugin, you need the marp-cli to build the presentation PDF (or html). See https://github.com/marp-team/marp-cli on how to install it on various systems.\n","title":"Marp: Create Presentations Using Markdown"},{"content":"It\u0026rsquo;s been over a year now that Symfony offers a new way of asset handling, the AssetMapper Component. This is a short guide on how to enable Bootstrap (styling, js and icons) when using it.\nInstallation \u0026amp; Setup Require the AssetMapper component with composer: composer require symfony/asset-mapper symfony/asset symfony/twig-pack\nUse the command provided by the AssetMapper to require Bootstrap\u0026rsquo;s assets:\nphp bin/console importmap:require bootstrap php bin/console importmap:require bootstrap/dist/js/bootstrap.min.js php bin/console importmap:require bootstrap-icons/font/bootstrap-icons.min.css Import the assets in your app.js: import \u0026#39;bootstrap/dist/css/bootstrap.min.css\u0026#39; import \u0026#39;bootstrap/dist/js/bootstrap.min.js\u0026#39; import \u0026#39;bootstrap-icons/font/bootstrap-icons.min.css\u0026#39; Usage Use Bootstrap as you are used to do, for example with a collapsable card:\n\u0026lt;p class=\u0026#34;mt-2 d-inline-flex gap-1\u0026#34;\u0026gt; \u0026lt;button class=\u0026#34;btn btn-outline-primary\u0026#34; type=\u0026#34;button\u0026#34; data-bs-toggle=\u0026#34;collapse\u0026#34; data-bs-target=\u0026#34;#collapseExample\u0026#34; aria-expanded=\u0026#34;false\u0026#34; aria-controls=\u0026#34;collapseExample\u0026#34;\u0026gt; Toggle Target \u0026lt;/button\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;div class=\u0026#34;collapse\u0026#34; id=\u0026#34;collapseExample\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;card card-body\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;mb-2\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;bi bi-info-circle-fill text-warning\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; \u0026lt;span class=\u0026#34;bg-warning-subtle\u0026#34;\u0026gt;Example warning\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; Placeholder Content \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ","date":"2024-05-18","permalink":"/symfony-assetmapper-bootstrap/","summary":"It\u0026rsquo;s been over a year now that Symfony offers a new way of asset handling, the AssetMapper Component. This is a short guide on how to enable Bootstrap (styling, js and icons) when using it.\nInstallation \u0026amp; Setup Require the AssetMapper component with composer: composer require symfony/asset-mapper symfony/asset symfony/twig-pack\nUse the command provided by the AssetMapper to require Bootstrap\u0026rsquo;s assets:\nphp bin/console importmap:require bootstrap php bin/console importmap:require bootstrap/dist/js/bootstrap.min.js php bin/console importmap:require bootstrap-icons/font/bootstrap-icons.min.css Import the assets in your app.js: import \u0026#39;bootstrap/dist/css/bootstrap.min.css\u0026#39; import \u0026#39;bootstrap/dist/js/bootstrap.min.js\u0026#39; import \u0026#39;bootstrap-icons/font/bootstrap-icons.min.css\u0026#39; Usage Use Bootstrap as you are used to do, for example with a collapsable card:\n","title":"Symfony AssetMapper \u0026 Bootstrap"},{"content":"TL; DR install dev dependencies needed to build packages with makepgk sudo pacman -S --needed base-devel Create a directory where you want to store your AUR packages and cd into it mkdir aur-packages cd aur-packages clone the repository you need and cd into it git clone insert_git_clone_url cd the_cloned_repo build the package and install the programm locally (documentation) makepkg -i -s -r -c What is the AUR? AUR is short for “Arch User Repository”. A repository is a storage location for software packages. You can retrieve the packages from it to install software. As you can derive from the name, this repository harbors package building instructions (PKGBUILDs) created by the community. The PKGBUILDs must follow the rules of submission.\nWhat is the difference to core/extra packages? As you can read in the official wiki\ncore contains everything needed to build a minimum functional Arch Linux OS extra holds useful, officially maintained software, such as window managers, web browsers etc. the AUR holds PKGBUILDs provided by anybody. The only requirement to do so is to be a registered user. If a package receives enough community interest and support by a package maintainer, it can be moved to be officially maintained in the extra repository. Security notice While the English wiki page warns you that everything you install from the AUR is “at your own risk”, you might wonder what you can do to assert if a PKGBUILD is trustworthy. The German wiki provides a neat checklist and examples to look out for.\nTip read the package’s comments in the AUR. Someone else might have encountered an issue already do not build the package as root check url= and source= check for harmful code ","date":"2024-03-09","permalink":"/arch-linux-how-to-install-aur-packages/","summary":"TL; DR install dev dependencies needed to build packages with makepgk sudo pacman -S --needed base-devel Create a directory where you want to store your AUR packages and cd into it mkdir aur-packages cd aur-packages clone the repository you need and cd into it git clone insert_git_clone_url cd the_cloned_repo build the package and install the programm locally (documentation) makepkg -i -s -r -c What is the AUR? AUR is short for “Arch User Repository”. A repository is a storage location for software packages. You can retrieve the packages from it to install software. As you can derive from the name, this repository harbors package building instructions (PKGBUILDs) created by the community. The PKGBUILDs must follow the rules of submission.\n","title":"Arch Linux How to Install AUR Packages"},{"content":"With 2024 just having started you might be tempted to make some New Year’s resolutions. Should your resolutions include finally making your own blog or website, you might profit from my experiences.\nThe setup I am using a managed hosting solution. What does this mean? You pay a provider for the “full package” you need to get your site up and running.\nThe minimum you need is:\nDomain and SSL certificate Hardware Resources (storage, memory, cpu) on which your WordPress will run on Often also included:\nsome backup strategy DDoS protection maleware scans After choosing your provider and product of choice, you will end up with a WordPress login for your WordPress admin backend.\nWhat you have to manage Your hosting provider provides the hardware and takes care of its functionality. During the setup, WordPress will also be installed and “linked” to your domain. If you do not specifically pay for it, you have to manage your WordPress, plugins and theme updates on your own. This also means keeping track of security issues and taking measures against vulnerabilities is your responsibility.\nCosts My personal setup includes my business e-mail and my domain as well as the simplest WordPress hosting plan. The prices vary but I think you should end up at about 20€ monthly costs depending on your needs.\nI ran with the free Cenote theme for the first three months but decided to upgrade to a lifetime pro license because I enjoy the theme. If you are happy with a free theme I recommend waiting for a sale, since the lifetime licenses can cost around several hundred euros.\nNoteworthy Apart from the updates and security you are also responsible for ensuring that your website meets legal requirements. I keep it simple and use only self produced images. Another low hanging fruit is to check that all sources you see in the network tab are loaded directly from your domain (otherwise you might need a cookie consent banner).\n","date":"2024-01-01","permalink":"/get-started-your-own-website/","summary":"With 2024 just having started you might be tempted to make some New Year’s resolutions. Should your resolutions include finally making your own blog or website, you might profit from my experiences.\nThe setup I am using a managed hosting solution. What does this mean? You pay a provider for the “full package” you need to get your site up and running.\nThe minimum you need is:\nDomain and SSL certificate Hardware Resources (storage, memory, cpu) on which your WordPress will run on Often also included:\n","title":"Get Started Your Own Website"},{"content":"A bit of time has already passed in December and in case you missed it, check out:\nhttps://adventofcode.com/\nAdvent of Code is a coding advent calendar. I can recommend it if you want to learn a new language, improve one you already know or if you are just up for the fun. How you solve the puzzles is fully up to you.\nThe pace is also yours to chose and you do not have to do the tasks in order – just like in a regular advent calendar you can open door eight before door 3 😉\nI fell behind quite a bit, maybe because I was baking too many christmas cookies, but that also is not a problem since the tasks stay online after December.\nWhere to start?! If you are little lost and new to programming, you might try solving the riddles in Python or Javascript. I tried Lua on some riddles last year, but you can chose whatever you like. If you are stuck or need help, you might try a Github search for Advent of Code or checkout the Reddit. As Rust is gaining popularity, you will also find exemplary solutions with it.\n","date":"2023-12-11","permalink":"/advent-of-code/","summary":"A bit of time has already passed in December and in case you missed it, check out:\nhttps://adventofcode.com/\nAdvent of Code is a coding advent calendar. I can recommend it if you want to learn a new language, improve one you already know or if you are just up for the fun. How you solve the puzzles is fully up to you.\nThe pace is also yours to chose and you do not have to do the tasks in order – just like in a regular advent calendar you can open door eight before door 3 😉\n","title":"Advent of Code"},{"content":"It has been roughly one month since I started using my Framework Laptop 13 DIY Edition (AMD Ryzen™ 7040 Series) with Arch Linux. The following post is about my experiences.\nGetting Started - Assembly It was a bit like early Christmas when my Framework 13 arrived. Since I chose the DIY edition I knew I would have to assemble some of it. The package made a solid impression and everything was neatly packed.\nWith the DIY edition you receive the device partly preassembled. That means you have to insert storage and memory and thus the keyboard is not attached. Because of the keyboard not being pre-mounted, the bezel is also not attached yet and you have to attach it.\nThe assembly is quite simple, since the installation locations are marked. If you are unsure about anything I strongly recommend following the manufacturer\u0026rsquo;s guides.\nWeak spot When I started attaching the bezel, I noticed that it would not fit fully on the bottom right corner. This also is the corner where the screen cables are located. The space is very narrow and you have to make sure that the cables are perfectly flat and fitting in order to attach the bezel correctly. Apart from that, everything went well.\nArch Linux I used archinstall to see how far I get. I chose the AMD graphics drivers and everything worked as expected and still does. I only had to install bluez-utils in order to get Bluetooth to work.\nConclusion I like the keyboard with clearly separated keys and the screen ratio of 3:2, since code happens in the vertical dimension. The laptop is new and I can go a whole workday without charging (if I do not participate in lots of video calls ;) ). The weak spot is something I stumbled upon during the assembly but so far I have no hardware issues and I open and close the laptop 4-6 times a day with an average usage of 6 days per week.\nRegarding the pricing (I chose a 1TB SSD and 64GB memory) I think you can get a comparable setup for a cheaper price. But running on Arch (with Gnome) I wanted an option without extra dedicated GPU, because I had trouble with it in the past. In addition to this I like the company\u0026rsquo;s attitude towards repair and sustainability.\nThe Framework 13 is the so far best match for me being a lightweight device, that combines a simple design with the hardware specs I need as a fullstack web developer.\n","date":"2023-12-08","permalink":"/framework-13-1-month-usage-review/","summary":"It has been roughly one month since I started using my Framework Laptop 13 DIY Edition (AMD Ryzen™ 7040 Series) with Arch Linux. The following post is about my experiences.\nGetting Started - Assembly It was a bit like early Christmas when my Framework 13 arrived. Since I chose the DIY edition I knew I would have to assemble some of it. The package made a solid impression and everything was neatly packed.\n","title":"Framework 13: Review"},{"content":"The Issue After a fresh installation of Arch Linux (using archinstall), my bluetooth was not working. The GUI toggle would toggle on and directly off again.\nThe Culprit Read: https://wiki.archlinux.org/title/bluetooth\nRun: pacman -Qs bluetooth\nThe package bluez-utils was missing from my system.\nThe Solution Install the missing package: pacman -Syu bluez-utils\nManual tryout: sudo systemctl start bluetooth.service\nEnable the service to auto-start it on startup: sudo systemctl enable bluetooth.service\n","date":"2023-11-30","permalink":"/fix-bluetooth-on-a-fresh-arch-linux-installation/","summary":"The Issue After a fresh installation of Arch Linux (using archinstall), my bluetooth was not working. The GUI toggle would toggle on and directly off again.\nThe Culprit Read: https://wiki.archlinux.org/title/bluetooth\nRun: pacman -Qs bluetooth\nThe package bluez-utils was missing from my system.\nThe Solution Install the missing package: pacman -Syu bluez-utils\nManual tryout: sudo systemctl start bluetooth.service\nEnable the service to auto-start it on startup: sudo systemctl enable bluetooth.service\n","title":"Fix Bluetooth on a Fresh Arch Linux Installation"},{"content":"When I set up a new laptop recently I went for Arch Linux with Gnome as desktop. After finishing the setup, I noticed that the default Gnome installation brings along more than I need. On the other hand, I performed my “post setup” installations to get my system ready for the daily developer’s work.\nThis post deals with the packages I removed and the basic set of packages I installed for my dev environment.\nTL;DR Use pacman -Qs gnome to list packages and remove what you dislike with pacman -Rsu package-name.\nWhat came along with Gnome? As outlined in the pacman documentation, use\npacman -Qs gnome to see a list of what comes along with it. Apart from the pure packages’ names you will also find a short explanation what the package is useful for (or not).\nWhat did I remove? I did not throw away everything, but there are some things I simply do not need. Removing those packages did not impair my system. Depending on your needs, you might want to remove more or less. You should be somewhat cautious on what you remove, since you can break your desktop.\nUsing\npacman -Rsu I removed:\ngnome-software gnome-music gnome-maps gnome-tour gnome-weather epiphany (a web browser) malcontent (parental control tooling) What did I add? I have a standard set of applications which I need (or want) to have on my system.\nPackage Used for firefox, chromium Web browser pkgstats anonymously send data to support https://pkgstats.archlinux.de libreoffice-still I need it for wokr (to deal with pptx for example) dbeaver database tool (GUI) zsh another shell (see https://wiki.archlinux.org/title/zsh) gnome-screenshot useful for screenshots, see https://wiki.archlinux.org/title/Screen_capture (chap. 1.2) ","date":"2023-11-29","permalink":"/gnome-bloatware-removal/","summary":"When I set up a new laptop recently I went for Arch Linux with Gnome as desktop. After finishing the setup, I noticed that the default Gnome installation brings along more than I need. On the other hand, I performed my “post setup” installations to get my system ready for the daily developer’s work.\nThis post deals with the packages I removed and the basic set of packages I installed for my dev environment.\n","title":"Gnome Bloatware Removal"},{"content":"Why should I do this? Why should I do this?\nThe days of going to a store and buying a Windows installation CD are a thing of the past. If you search for it, you can find buyable USB sticks to install an OS but you can also simply create them and learn something along the way.\nPrerequisites 1. You need an USB stick\nI experienced a lot of issues with cheap sticks of no-name brands (offers like 10 sticks for 30€) so I stick to SanDisk, Kingston or Intenso.\n2. You need an ISO\nISO is short for \u0026ldquo;Optical Disc Image\u0026rdquo;. For our use case think of the ISO file as a downloadable file containing everything you need to install the operating system (OS) it was made of/for. Think: operating system ISO = OS image. Depending on the OS you want to try out, you should refer to the official documentation. Some distributions recommend programs that aid you in writing the ISO to a stick (= flash the ISO), but you can also do it via the terminal. The following table shows some official sites plus their recommendations for programs that help you flash an ISO to SD cards/USB drives.\nOperating System Recommended Program(s) Official Guide ISO Download Ubuntu balenaEtcher: works on Windows, MacOS \u0026amp; Linux\nRufus: Windows only Ubuntu Guide Ubuntu Download Debian Officially: win32diskimager\nI used to use Rufus but balenaEtcher made a good first impression Debian General Guide Debian Guide Write USB Debian Download Arch Linux In the wiki, a lot of different options are explained ranging from 3rd party programs with a GUI to terminal tools like Cygwin or the manual approach with command line utilities which are part of Arch\u0026rsquo;s coreutils Arch Linux Guide Arch Linux Download Linux Mint USB Image Writer Linux Mint Guide Linux Mint Download Flash the USB 3. Use a program to flash your USB\nThe programs mentioned above have their own tutorials/guides. Check them out! :)\n4. Create a bootable stick manually using coreutils\nHost OS: Linux ISO: Arch Linux\n4a.) Download the latest ISO\ninternational: https://archlinux.org/download/ -\u0026gt; in case of doubt, use the Worldwide mirror http://geo.mirror.pkgbuild.com and select the x86_64.iso\nGerman: https://www.archlinux.de/download -\u0026gt; use the handy download button\n4b.) Obtain data to verify the ISO\nIn the case of Arch Linux you can find the relevant information (PGP key download, sha256 sum) directly on the download page. Other distributions have the information located somewhere else. Check the list below to find your guide/checksum for the ISO you chose.\nUbuntu -\u0026gt; https://ubuntu.com/tutorials/how-to-verify-ubuntu#1-overview Debian -\u0026gt; https://www.debian.org/releases/bullseye/amd64/ch04s07.en.html Linux Mint -\u0026gt; https://linuxmint-installation-guide.readthedocs.io/en/latest/verify.html 4c.) Verifiy the ISO\nWhy? With this check you ensure that the file you downloaded is an exact copy of what is present at the official download servers. If this check fails this can mean that the file is damaged which could result in random issues during the installation. It can also mean that you somehow ended up with a file not being the official file of the developers but of someone else, which in the worst case is a modified or malignant copy.\nSHA256 Checksum SHA-256 is a cryptographic hash function. If applied to a file, the algorithm outputs a unique hash value - the checksum of the file. This checksum - a string - can be used to verify the integrity of a file.\nFirst, generate the sha256 sum of the ISO:\nsha256sum archlinux-2023.11.01-x86_64.iso This command will output a long string of characters and the ISO file\u0026rsquo;s name behind that long string.\n477f50617d648e46d6e326549aa56ab92115a29a97f2ca364e944cea06970608 archlinux-2023.11.01-x86_64.iso Compare your output to the sha256 sum on the official website. If they are identical, the downloaded ISO file has not been modified.\nGPG/PGP Signature Note: you need the package extra/sequoia-sq\nsudo pacman -Syu sequoia-sq Both GPG and PGP are cryptocraphic software that can be used to sign (and verify) files. The publisher/developer of the file signs it using their private key. The public key can be used by and enduser to verify the signature.\nDownload the signature by clicking on the link at the official download page.\nYou will find a .sig file in your downloads folder or the location you specified. For the later verification it is best to have the ISO file and the .sig file in the same directory. Download the release signing key with WKD:\nsq wkd get pierre@archlinux.org -o release-key.pgp Verify the signature:\nsq verify --signer-file release-key.pgp --detached archlinux-2023.11.01-x86_64.iso.sig archlinux-2023.11.01-x86_64.iso If the signature is valid, the ISO is authentic and can be used safely. 4d.) Write ISO to USB Flash Drive using dd\nFind out the name of the USB flash drive:\nls -l /dev/disk/by-id/usb-* If your drive is recognized by the system, you should see an output like the following: If you already setup a bootable USB flash drive, the output looks like below. In that case, you can use that stick directly. Check that the drive is not mounted:\nlsblk The output of lsblk shows the block devices recognized by your system. Recognized is not mounted. The column MOUNTPOINTS gives info if the drive is mounted or not. Something being \u0026ldquo;mounted\u0026rdquo; means it is attached to the file system hierarchy, that starts with a forward slash. If you do not see mountpoints for your drive, it is not mounted as in the example below. write the ISO to the stick using dd:\nI simply cd into the directory where I saved the iso. If you are elsewhere, if (= in-file) takes the path to the ISO file. Also you have autocompletion on tab in your terminal which prevents typos in the values you set at in file and out-file (of).\nsudo dd bs=4M if=archlinux-version-x86_64.iso of=/dev/disk/by-id/usb-My_flash_drive conv=fsync oflag=direct status=progress Further reading: https://wiki.archlinux.org/title/USB_flash_installation_medium https://www.debugpoint.com/verify-iso-files-linux/ Distrowatch: Top 10 Linux Distributions ","date":"2023-11-11","permalink":"/how-to-create-a-bootable-usb-stick/","summary":"Why should I do this? Why should I do this?\nThe days of going to a store and buying a Windows installation CD are a thing of the past. If you search for it, you can find buyable USB sticks to install an OS but you can also simply create them and learn something along the way.\nPrerequisites 1. You need an USB stick\nI experienced a lot of issues with cheap sticks of no-name brands (offers like 10 sticks for 30€) so I stick to SanDisk, Kingston or Intenso.\n","title":"How to create a bootable USB stick"},{"content":"Prerequisites Download the latest Arch ISO. I refer to the clickable link on the official releases page or - for German users - you can navigate to archlinux.de/downloads\nCheck the integrity of the downloaded ISO using the guide you can find on archlinux.org/downloads in the section \u0026ldquo;HTTP direct downloads\u0026rdquo;\nConfigure a bootable USB stick.\nAfter booting your stick and chosing our preferred keyboard layout, you need to connect to the internet\nInstalling Arch Linux Boot the USB stick First, boot the USB stick on the computer where you want to install Arch Linux. You should be seeing below output. Load your preferred keyboard layout, for example loadkeys de-latin1 for German.\nConnect to a router with a LAN cable directly or connect to your WLAN.\nConnecting to WLAN I use the following commands to wirelessly connect to my router. Each command produces output as you can see in the image below.\niwctl device list station replace_with_your_device get-networks station replace_with_your_device connect replace_with_your_WLAN_SSID exit In order to test your connection, you can ping google.com for example. The connection is established if you see ongoing responses of \u0026ldquo;x bytes received from\u0026hellip;\u0026rdquo;\nArchinstall Run archinstall. You should see the interactive menu in the console. Configuratio The following is an exemplary configuration I use. With Arch not everything is handed to you ready-made on a silver platter, yet if you are up for gaining the necessary background knowledge, it will pay out on the long run. Thus, if you stumble across something unfamiliar in the setup options: do some research, fill your knowledge gaps \u0026amp; improve :)\nConfig Option Value Additional Info Archinstall language your preferred language Mirrors region close to you What you want to install must come from somewhere. Mirrors are servers providing \u0026ldquo;all the stuff\u0026rdquo; you need to install Arch Linux. You can chose a mirror close to your location, since geographical proximity means low latency. Locales your locale I chose \u0026ldquo;de\u0026rdquo; since I have a German keyboard Disk configuration best-effort layout filesystem: ext4 I keep it simple and I am not doing much of manual effort here. Disk encryption skip Bootloader Systemd-boot systemd-boot Swap keep default (True) Hostname the name you want for your PC how about archibald ;) Root Password your chosen root password Root User account add users as you need it Since I am the only user I always create one admin user with root privileges Profile Desktop: what you prefer, gnome is quite popular If you do not select \u0026ldquo;Desktop\u0026rdquo; here, you will have an installation with a command line only and not with the GUI you are used to where you can login and install and use programs like web browsers or office programs. Audio Pipewire your choice shouldn\u0026rsquo;t matter if you are a regular user Kernels Linux Additional packages Skip I think I always ended up missing packages. But I tend to install them without a hurry afterwards Network configuration Use NetworkManager Timezone your timezone You can use the search to easily select your zone. For me it is Europe/Berlin. Automatic time sync (NTP) keep default (True) Optional repositories skip ","date":"2023-10-28","permalink":"/arch-linux-in-2023-simple-installation-guide/","summary":"Prerequisites Download the latest Arch ISO. I refer to the clickable link on the official releases page or - for German users - you can navigate to archlinux.de/downloads\nCheck the integrity of the downloaded ISO using the guide you can find on archlinux.org/downloads in the section \u0026ldquo;HTTP direct downloads\u0026rdquo;\nConfigure a bootable USB stick.\nAfter booting your stick and chosing our preferred keyboard layout, you need to connect to the internet\nInstalling Arch Linux Boot the USB stick First, boot the USB stick on the computer where you want to install Arch Linux. You should be seeing below output. ","title":"Arch Linux in 2023 Simple Installation Guide"},{"content":"There exists a colorful bouquet of operating systems (OS) to chose from. Most people know Windows and MacOS. If you google for “the most popular Linux distribution” you will very likely find Ubuntu, Manjaro or Debian in the lists of other bloggers who often rely on distrowatch.com and there are enough articles out there about “what is the best Linux distro for xyz”.\nWhen I first started to use Linux, I did not start with Arch Linux. I switched to it in the beginning of this year (2023) and it gave me grey hair and painful learnings. Yet I would never want to change that. Starting to use Arch Linux meant starting to need to know what I am doing. If you are that kind of person, you might like Arch Linux as much as I do.\nIf you google for “Arch Linux install” you will come across the official wiki (sidenote: you should always refer to the official documentation). The installation guide is good but at first glance it can be intimidating, especially if you are new to the developer world. Thus I will share my simple yet effective checklist for installing Arch Linux from a bootable USB Stick using archinstall in the next blog post.\nStay tuned and have a good weekend 🙂\n","date":"2023-10-27","permalink":"/for-the-love-of-arch/","summary":"There exists a colorful bouquet of operating systems (OS) to chose from. Most people know Windows and MacOS. If you google for “the most popular Linux distribution” you will very likely find Ubuntu, Manjaro or Debian in the lists of other bloggers who often rely on distrowatch.com and there are enough articles out there about “what is the best Linux distro for xyz”.\nWhen I first started to use Linux, I did not start with Arch Linux. I switched to it in the beginning of this year (2023) and it gave me grey hair and painful learnings. Yet I would never want to change that. Starting to use Arch Linux meant starting to need to know what I am doing. If you are that kind of person, you might like Arch Linux as much as I do.\n","title":"For the Love of Arch"},{"content":"The moment you start collaborating on code you will inevitably encouter \u0026ldquo;git\u0026rdquo;. Git is a version control system. That is the buzzword you might need for your application and interviews. This post covers the basic principle behind git, some very neat interactive tutorials and sources to deepen your knowledge on git.\nWhat? Version control is a mechanism that allows you to store the state of your code (snapshot) at a certain point of time and this mechanism also allows you to go back and forth between snapshots. The save points are called \u0026ldquo;commits\u0026rdquo;. The folder structure of your code with everything in it is a repository. You might come across GitLab or GitHub. Those two are platforms that allow sharing repositories and collaborating on projects.\nHow? So, when you are new to coding, you might be familiar with the way you did version control when - for example - writing a thesis or essay. You might have ended up like me: attaching an \u0026ldquo;_v1\u0026rdquo;, \u0026ldquo;_v2\u0026rdquo; et cetera to your filenames and always saving whole files anew.\nWhen it comes to coding projects or even software, some KB (as in the case of textfiles) will not be what you see. If platforms like GitLab stored every file you have in a project at a certain point of time it would take up a lot of disk space. Thus, only the delta - the set of changes - between two snapshots is stored: the commit. Internally, more complex processes are going on but for a start this is enough for you to know as well as the fact that git refers a commit by its SHA-1 hash.\nUnder the hood, git actually builds up a tree-like structure of hashes referencing hashes to keep track of your and other\u0026rsquo;s work and to offer all the commands to \u0026ldquo;move backward and forward in time\u0026rdquo;.\nIf you are interested in gaining deeper knowledge I think Chapter 10 of the Pro Git book might be what you are looking for.\nLearn it - \u0026ldquo;git good\u0026rdquo; Before I extend this post with explanations of branching, merging and rebasing, check out those two interactive tutorials/games. The first one is one I do on a regular basis: learngitbranching. I do it to refresh my knowledge because I tend to fall back to some few commands I use every time although there could be easier combinations or single commands. The tutorial is available in German as well, you only have to change the locale to \u0026ldquo;de_DE\u0026rdquo;.\nhttps://learngitbranching.js.org/?locale=en_US\nLearngitbranching runs in the browser and has short explanatory sequences on the various git commands. Afterwards you will play levels with increasing difficulty. The game itself works with presenting you a goal and you have to enter the right commands to reach that desired state. Ohmygit is a downloadable and playable game. You have \u0026ldquo;cards\u0026rdquo; you can play to follow the instructions of each level, or you can directly type the commands. The levels are wrapped up with funny stories, so this tutorial is a bit more playful than the first one.\nhttps://ohmygit.org/\nFurther Reading https://phoenixnap.com/kb/how-git-works -\u0026gt; covers the commands with explanations https://git-scm.com/book/en/v2 -\u0026gt; covers history, setup, commands, internals and more ","date":"2023-10-24","permalink":"/what-is-git/","summary":"The moment you start collaborating on code you will inevitably encouter \u0026ldquo;git\u0026rdquo;. Git is a version control system. That is the buzzword you might need for your application and interviews. This post covers the basic principle behind git, some very neat interactive tutorials and sources to deepen your knowledge on git.\nWhat? Version control is a mechanism that allows you to store the state of your code (snapshot) at a certain point of time and this mechanism also allows you to go back and forth between snapshots. The save points are called \u0026ldquo;commits\u0026rdquo;. The folder structure of your code with everything in it is a repository. You might come across GitLab or GitHub. Those two are platforms that allow sharing repositories and collaborating on projects.\n","title":"What Is Git"},{"content":"Today I want to put the spotlight on some YouTube channels that might help you in your web developer journey. When starting as a web developer, finding time to learn can be challenging. The following list hopefully provides some orientation and saves you some time.\nLearn Web Development I have not done a full course by myself, but I tuned in some videos of Web Dev Simplified and his learning tracks might be what you need. He offers a Full Stack Web Dev Course including videos such as “How The Backend Works“, “What is REST?” and “MVC Explained in 4 Minutes“.\nHTML \u0026amp; CSS I can only point out Kevin Powell’s channel. Kevin is a sympathetic creator and offers a variety of content. He focuses on CSS, which comes along with HTML. Tracks I can recommend for a start:\nHTML \u0026amp; CSS for beginners The only tags you need when first learning HTML The 6 most important CSS concepts for beginners Stay up to Date in the Webdev World The web is moving fast. New frameworks are around every corner and browsers are also subject to constant changes and updates. The following channels are channels I consume to stay up to date:\nChrome for Developers: what’s new in the web and in the Chrome Dev Tools HTTP 203 videos: topics are explored in dialog format Fireship: coding tutorials \u0026amp; a neat “xyz in 100 seconds” format Awesome: monthly news, first impressions on hyped topics and a useful “xyz explained in 5 minutes” format ","date":"2023-10-22","permalink":"/useful-youtube-channels/","summary":"Today I want to put the spotlight on some YouTube channels that might help you in your web developer journey. When starting as a web developer, finding time to learn can be challenging. The following list hopefully provides some orientation and saves you some time.\nLearn Web Development I have not done a full course by myself, but I tuned in some videos of Web Dev Simplified and his learning tracks might be what you need. He offers a Full Stack Web Dev Course including videos such as “How The Backend Works“, “What is REST?” and “MVC Explained in 4 Minutes“.\n","title":"Useful Youtube Channels"},{"content":"The list below contains sites providing tooling I use in my day-to-day tasks as a web developer. I will keep it up to date.\nhttps://developer.mozilla.org/\nWhenever I google “how to accomplish xyz in html/css/js” I use this site as reliable source\nhttps://caniuse.com/\n“Can I use” provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers.\nhttps://regex101.com/\nbuild, test and debug regular expressions\nhttps://youmightnotneedjquery.com/\nWhenever I stumble accross legacy jQuery code, this site can help in finding out how to replace it\n","date":"2023-10-16","permalink":"/useful-tools-sites/","summary":"The list below contains sites providing tooling I use in my day-to-day tasks as a web developer. I will keep it up to date.\nhttps://developer.mozilla.org/\nWhenever I google “how to accomplish xyz in html/css/js” I use this site as reliable source\nhttps://caniuse.com/\n“Can I use” provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers.\nhttps://regex101.com/\nbuild, test and debug regular expressions\nhttps://youmightnotneedjquery.com/\n","title":"Useful Tools \u0026 Sites"},{"content":"I am not going to bother you with the things we all know: everyone learns different, how to make a learning schedule et cetera. This post will deal with good sources to start learning. Jet you have to keep in mind that they might not be the most fitting depending on your tech stack and the tech stack your company applies. Moreover, I only cover languages, frameworks and tools I have used.\nPHP and major PHP frameworks When I started to learn web development I was working in a company that used PHP. I know PHP does not have the best reputation but it has changed. Compared to other languages, PHP as well as the major frameworks associated with it (Laravel, Symfony) had the time to learn from mistakes and make improvements. PHP is widely used and you can learn a lot of core concepts in just one udemy course: PHP for beginners. I know this course is becoming a bit dusty but it is still worth a try. If you are new to udemy you should know that the platform has sales very often. Do not buy courses for the full price, just wait a bit if you can afford the time.\nIf your company even uses Laravel or Symfony, I can only recommend the official tutorials of those two: Laracasts and SmyfonyCasts. Both offer PHP learning content as well, so the framework specific courses could be enough and you might not even need udemy.\nJS Frameworks The modern web development world is a world of Javascript frameworks. I mainly worked with Vue.js and I learned it hands on from other developers. There is an official learning platform, Vue Mastery, which – imho – is rather for beginners. Compared to SymfonyCasts which have “knee deep into the framework” courses, Vue Mastery is good for someone with less knowledge but not for someone who searches detail answers on framework internals.\nApart from videos, most bigger frameworks or tools offer an official documentation. It is always best to refer to the official docs rather to any stack overflow post. But we all know it: writing documentation is neither easy nor do people seem to love it, especially when it comes to good, useful documentation. This means documentation can be wrong or outdated, too. Especially in the fast-moving world of JS frameworks, documentation is sometimes overtaken by reality. In case of doubt it is worth to check the project’s Github for open issues or discussions. You can also start a discussion if you are not sure. In maintained/active projects, you should recieve an answer the sooner or later. And if nothing helps, you can still check the project code itself.\nBooks For getting a general overview of things on a topic I can recommend the “for dummies” (ger.: für dummies) book series by Wiley. It will not teach you everything fully but I think the books are suitable for a shallow introduction to topics and they helped me a bunch regarding to learn the correct wordings. If you have a specific interest feel free to contact me on a topic and maybe I can help you out.\n","date":"2023-10-16","permalink":"/experiences-learn-to-code/","summary":"I am not going to bother you with the things we all know: everyone learns different, how to make a learning schedule et cetera. This post will deal with good sources to start learning. Jet you have to keep in mind that they might not be the most fitting depending on your tech stack and the tech stack your company applies. Moreover, I only cover languages, frameworks and tools I have used.\n","title":"Experiences Learn to Code"},{"content":" This is the first post in an - almost - empty WordPress project. I am happy you stumbled accross my page and I hope you will return! What you should return for? You will find posts and articles helpful for web developers with a focus on cross-background beginners. Why? Because that is where I started, too, and I often came across the famous \u0026ldquo;I wish I had known that\u0026rdquo; moments.\n","date":"2023-10-16","permalink":"/hello-world/","summary":" This is the first post in an - almost - empty WordPress project. I am happy you stumbled accross my page and I hope you will return! What you should return for? You will find posts and articles helpful for web developers with a focus on cross-background beginners. Why? Because that is where I started, too, and I often came across the famous \u0026ldquo;I wish I had known that\u0026rdquo; moments.\n","title":"Hello World!"}]