Files
server_cloud_ladkau_de/docs/runbook.md
T
ml 75a0f21f74 Implemented all service roles
- docker: install Docker Engine from official apt repo, configure log rotation,
  create shared traefik_public network
- traefik: reverse proxy with automatic Let's Encrypt TLS, dashboard on
  cloud.ladkau.de behind basic auth
- gitea: self-hosted Git with PostgreSQL, Git-over-SSH on port 2222
- sso: Keycloak with PostgreSQL for OIDC/OAuth2 single sign-on
- nextcloud: file storage with PostgreSQL, Redis, cron sidecar, CalDAV/CardDAV
  well-known redirects
- mail: Roundcube webmail client with PostgreSQL
- registry: Docker Registry v2 with htpasswd auth, no upload size limit
- k8s: placeholder nginx page

All secrets documented in vault.yml; runbook updated with per-service
first-run notes.
2026-06-27 15:35:15 +02:00

4.0 KiB

Reinstall Runbook

Follow these steps to provision a fresh server from scratch.

Prerequisites

On your local machine:

  • Ansible installed (pip install ansible)
  • SSH access to the server as root using keys/root_cloud_ladkau_de

Steps

1. Install Ansible collections

From the repo root:

ansible-galaxy collection install -r ansible/requirements.yml

Required collections:

  • community.general — ufw, timezone modules
  • ansible.posix — authorized_key module

2. Initial root login

Connect as root and verify the server is reachable:

ssh -i keys/root_cloud_ladkau_de root@217.154.207.148

3. Create the deploy user (one-time, manual)

On the server as root:

adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
cat >> /home/deploy/.ssh/authorized_keys <<'EOF'
<paste contents of keys/notroot_cloud_ladkau_de.pub>
EOF
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

4. Set secrets (Ansible Vault)

Before running the playbook, populate secrets that must not be committed in plain text. Create ansible/group_vars/vault.yml (gitignored) and encrypt it with Ansible Vault:

ansible-vault create ansible/group_vars/vault.yml

Required secrets:

# Traefik dashboard basic auth — generate with:
# echo $(htpasswd -nB admin) | sed -e 's/\$/\$\$/g'
traefik_dashboard_users: "admin:$$2y$$05$$..."

# Gitea — generate secrets with: openssl rand -hex 32
gitea_db_password: ""
gitea_secret_key: ""
gitea_internal_token: ""

# Keycloak
keycloak_db_password: ""
keycloak_admin_password: ""

# Nextcloud
nextcloud_db_password: ""
nextcloud_admin_password: ""

# Roundcube — des_key must be exactly 24 characters: openssl rand -hex 12
roundcube_db_password: ""
roundcube_des_key: ""

# Container registry — generate with:
# docker run --entrypoint htpasswd httpd:2 -Bbn <user> <password>
registry_htpasswd: "user:$2y$05$..."

Registry usage after deploy:

# Login
docker login cr.ladkau.de

# Push an image
docker tag myimage:latest cr.ladkau.de/myimage:latest
docker push cr.ladkau.de/myimage:latest

# Pull an image
docker pull cr.ladkau.de/myimage:latest

Gitea first-run note: gitea_disable_registration defaults to true in group_vars/all.yml. For the very first deploy, override it to false in vault.yml so the Gitea setup wizard can create the admin account. After the admin account exists, remove the override and redeploy.

Keycloak first-run note: The KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORD environment variables bootstrap the initial admin account on first start only. After logging in at https://sso.ladkau.de, change the admin password via the Keycloak UI and remove the keycloak_admin_password reference from vault (or rotate it). Keycloak ignores these env vars once the admin account already exists.

To run the playbook with vault:

ansible-playbook -i ansible/inventory.ini ansible/site.yml --ask-vault-pass

5. Run the Ansible master playbook

From the repo root:

ansible-playbook -i ansible/inventory.ini ansible/site.yml

This runs all roles in order:

  1. base — OS hardening, SSH config, ufw firewall
  2. docker — Docker Engine + Compose plugin
  3. traefik — reverse proxy, TLS via Let's Encrypt
  4. gitea — self-hosted Git
  5. nextcloud — file storage
  6. sso — Single Sign-On
  7. mail — mail server
  8. registry — container registry
  9. k8s — k3s node

6. DNS

Ensure the following DNS A records point to 217.154.207.148 before running:

  • cloud.ladkau.de
  • gitea.ladkau.de
  • nextcloud.ladkau.de
  • sso.ladkau.de
  • mail.ladkau.de
  • cr.ladkau.de
  • k8s.ladkau.de

Traefik will attempt ACME certificate issuance on first start; DNS must resolve first.

Re-running after changes

The playbook is idempotent. To apply a single role only:

ansible-playbook -i ansible/inventory.ini ansible/site.yml --tags <role>