Files
oswald-infra/butane/oswald_runner.yaml.tftpl
T
2026-09-23 22:14:24 +00:00

130 lines
5.2 KiB
Plaintext

variant: flatcar
version: 1.0.0
systemd:
units:
# Gitea Actions runner. Registers once on first start and keeps its
# identity (the .runner file) in /var/lib/oswald_runner on the VM's own
# root disk -- not ephemeral, so it does not self-delete after each job
# (that caused stale offline entries whenever the container was
# interrupted before finishing a job; see flatcar-todo.md). Reusing the
# same registration also means the token never needs rotating.
#
# /var/lib/oswald_runner survives container restarts and VM reboots (it's
# on disk, not in the container), but not a VM rebuild (taint): that
# recreates the disk from the image, so the runner re-registers fresh
# next boot. Old entries from before this change, or from any future
# rebuild, still need manual cleanup in Gitea's runner admin page.
#
# GITEA_RUNNER_REGISTRATION_TOKEN is templated in from Infisical
# (GITEA_RUNNER_TOKEN in secrets.tmpl) by main.tf, not stored here. It
# still ends up in plaintext in the rendered Ignition snippet on the
# node's storage -- see the caveat in flatcar-todo.md.
- name: oswald_runner.service
enabled: true
contents: |
[Unit]
Description=Gitea Actions runner
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force oswald_runner
ExecStart=/usr/bin/docker run --name oswald_runner \
-e GITEA_INSTANCE_URL=https://git.atlashorizon.net \
-e GITEA_RUNNER_REGISTRATION_TOKEN=${gitea_runner_token} \
-e GITEA_RUNNER_NAME=oswald_runner \
-v /var/lib/oswald_runner:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
docker.io/gitea/runner:3
ExecStop=/usr/bin/docker stop oswald_runner
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
# n8n workflow automation, served publicly at n8n.atlashorizon.net.
#
# Prerequisites, all of which must exist before the VM is rebuilt:
# - an "n8n" Postgres database and role on argo.lan.atlashorizon.net
# - N8N_DB_PASSWORD and N8N_ENCRYPTION_KEY in Infisical (see
# secrets.tmpl); generate the key with `openssl rand -hex 32`
# - n8n.atlashorizon.net terminating TLS and proxying to this host's
# :5678 (not managed here)
#
# Workflows, credentials and execution history all live in Postgres, so
# a taint loses nothing. The encryption key is what makes that true:
# without a fixed one n8n generates its own under /home/node/.n8n, and
# the credentials in Postgres become undecryptable the moment the VM
# disk goes. Never rotate it without re-entering every credential.
- name: n8n.service
enabled: true
contents: |
[Unit]
Description=n8n workflow automation
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=/usr/bin/docker pull docker.n8n.io/n8nio/n8n:2.40.5
ExecStartPre=-/usr/bin/docker rm --force n8n
# /var/lib/n8n holds only what is left in /home/node/.n8n once the
# database and key are external: installed community nodes and
# n8n's own config file. Losing it to a taint means reinstalling
# community nodes, nothing more.
#
# N8N_PROXY_HOPS=1: requests arrive through the reverse proxy, so
# n8n takes the client IP from X-Forwarded-For (rate limiting and
# audit logs would otherwise see only the proxy).
ExecStart=/usr/bin/docker run --name n8n \
-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_HOST=argo.lan.atlashorizon.net \
-e DB_POSTGRESDB_PORT=5432 \
-e DB_POSTGRESDB_DATABASE=n8n \
-e DB_POSTGRESDB_USER=n8n \
-e DB_POSTGRESDB_PASSWORD=${n8n_db_password} \
-e N8N_ENCRYPTION_KEY=${n8n_encryption_key} \
-e N8N_HOST=n8n.atlashorizon.net \
-e N8N_PROTOCOL=https \
-e WEBHOOK_URL=https://n8n.atlashorizon.net/ \
-e N8N_PROXY_HOPS=1 \
-v /var/lib/n8n:/home/node/.n8n \
-p 5678:5678 \
docker.n8n.io/n8nio/n8n:2.40.5
ExecStop=/usr/bin/docker stop n8n
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
- name: dozzle-agent.service
enabled: true
contents: |
[Unit]
Description=Dozzle Agent
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force dozzle-agent
ExecStart=/usr/bin/docker run --name dozzle-agent \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 7007:7007 \
amir20/dozzle:v11.1.1 agent
ExecStop=/usr/bin/docker stop dozzle-agent
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
storage:
directories:
- path: /var/lib/oswald_runner
mode: 0755
# The n8n image runs as the "node" user (uid 1000), which needs to own
# its data directory.
- path: /var/lib/n8n
mode: 0700
user:
id: 1000
group:
id: 1000