From e5ade4b3ba00bb4e22e5eedfd5e74ef7db990553 Mon Sep 17 00:00:00 2001 From: aaron Date: Sat, 26 Sep 2026 13:50:13 +0200 Subject: [PATCH] feature: port the entire project to k3s --- .dockerignore | 10 ++++++++ .gitignore | 5 ++++ Dockerfile | 11 +++++++++ README.md | 57 +++++++++++++++++++++++++++++++++++++++++++- devenv.lock | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 41 ++++++++++++++++++++++++++++++++ devenv.yaml | 4 ++++ 7 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ea2a0fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.devenv/ +.direnv/ +devenv.lock +devenv.local.nix +devenv.nix +devenv.yaml +Dockerfile +.dockerignore +README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65b0564 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# devenv +.devenv/ +.devenv.flake.nix +devenv.local.nix +.direnv/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..30e280f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# Static greeter. Stock nginx serves /usr/share/nginx/html with no extra config; +# index.html, robots.txt and favicon.ico are picked up by the defaults. +FROM nginx:1.29-alpine + +# Content is baked into the image rather than bind-mounted (as the old +# docker-compose setup did), so a rollout is just a new tag. +COPY index.html robots.txt favicon.ico /usr/share/nginx/html/ +COPY _css/ /usr/share/nginx/html/_css/ +COPY _img/ /usr/share/nginx/html/_img/ + +EXPOSE 80 diff --git a/README.md b/README.md index 75a6be4..128ea30 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,58 @@ # website -Simple greeter. +Simple greeter — the landing page at [0x29a.ch](https://0x29a.ch). + +Previously a stock `nginx` container on the old Docker host, serving a bind-mounted +`/mnt/docker/website/html`. It now ships as a self-contained image (content baked in) +and runs on the k3s cluster behind the Hetzner Caddy. + +## Layout + +``` +index.html the greeter +_css/styles.css styling +_img/ft.jpg background image +favicon.ico favicon +robots.txt disallow all +Dockerfile nginx:1.29-alpine + the static files +devenv.nix dev shell: podman +``` + +## Build & push the image + +Images live in the Gitea container registry under `git.0x29a.ch/aaron/website`. +`podman` reads the `Dockerfile` natively — a drop-in for `docker`. + +```sh +podman build -t git.0x29a.ch/aaron/website:1.0.0 . +podman login git.0x29a.ch # Gitea user + a token with write:package +podman push git.0x29a.ch/aaron/website:1.0.0 +``` + +Run it locally to smoke-test: + +```sh +podman run --rm -p 8080:80 git.0x29a.ch/aaron/website:1.0.0 +``` + +## Deploy to k3s + +Deployed via the Helm chart in `helm-k3s-lab-gitops/apps/website`, synced by ArgoCD. +The package is private, so the namespace needs a pull secret referenced by the chart's +`image.pullSecret` (default `gitea-registry`): + +```sh +kubectl create secret docker-registry gitea-registry \ + --docker-server=git.0x29a.ch \ + --docker-username= \ + --docker-password= \ + -n website +``` + +Then bump `image.tag` in the chart's `values.yaml` and commit; ArgoCD rolls it out from +`main`. (Make the Gitea package public instead and set `image.pullSecret: ""` to drop the +secret entirely.) + +Traefik holds Ingress rules for both `0x29a.ch` and `www.0x29a.ch`. TLS is terminated at +the edge by Caddy on the Hetzner box, which reverse-proxies to the three k3s nodes over +Tailscale — see `ansible-play-infra/inventory/host_vars/0x29a.ch.yml`. diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..c1e6156 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,65 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1790415795, + "narHash": "sha256-nvpxhuC///gvHzRj9/8A+3+2frKEP8kaabBuQDRO4G4=", + "owner": "cachix", + "repo": "devenv", + "rev": "6d76db3889deba50ba7d906ec710d59fcd86354a", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1789506231, + "narHash": "sha256-vNkJbtmqyBfFhn/HnzQo4ROuoyYWuy/allqTfJb4kMY=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "c2f38fe7f9e04d9aadd354d380f2bd40531d9737", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1789370336, + "narHash": "sha256-6RSEDHIWQtesQKWSu5qRai8L2h4KgCgMEfJHstW99G4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c7def046b9a883d46974757852106483d741586f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} \ No newline at end of file diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..af0eccc --- /dev/null +++ b/devenv.nix @@ -0,0 +1,41 @@ +{ pkgs, ... }: + +let + # nixpkgs' rootless podman ships without /etc/containers config, so short + # names (nginx:1.29-alpine) and the signature policy can't resolve. These are + # the two files podman looks for under ~/.config/containers. + registriesConf = pkgs.writeText "registries.conf" '' + unqualified-search-registries = ["docker.io"] + ''; + policyJson = pkgs.writeText "policy.json" '' + { "default": [ { "type": "insecureAcceptAnything" } ] } + ''; +in +{ + # Static site — no language toolchain needed, just the image build. + packages = [ + # --- build & ship the image --- + pkgs.podman # `podman build -t git.0x29a.ch/aaron/website:1.0.0 .` + # # reads the Dockerfile natively; push with `podman push`. + ]; + + enterShell = '' + # Seed rootless-podman config once, so `podman build` resolves short image + # names and finds a signature policy. Never clobbers existing user config. + conf="$HOME/.config/containers" + if [ ! -e "$conf/registries.conf" ] || [ ! -e "$conf/policy.json" ]; then + mkdir -p "$conf" + [ -e "$conf/registries.conf" ] || install -m644 ${registriesConf} "$conf/registries.conf" + [ -e "$conf/policy.json" ] || install -m644 ${policyJson} "$conf/policy.json" + echo "seeded rootless-podman config in $conf" + fi + + # Persist registry logins. Rootless podman defaults to the volatile + # $XDG_RUNTIME_DIR (wiped on reboot); point it at ~/.config so a single + # `podman login git.0x29a.ch` survives reboots. The token lives in + # auth.json (base64, outside the repo) — never committed. + export REGISTRY_AUTH_FILE="$HOME/.config/containers/auth.json" + + echo "website devenv ready — build: podman build -t git.0x29a.ch/aaron/website:1.0.0 ." + ''; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..707015a --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,4 @@ +# devenv inputs. Commit this and devenv.lock; .devenv/ stays gitignored. +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling