# 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: ```bash 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: ```bash adduser deploy usermod -aG sudo deploy mkdir -p /home/deploy/.ssh cat >> /home/deploy/.ssh/authorized_keys <<'EOF' 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: ```bash ansible-vault create ansible/group_vars/vault.yml ``` Required secrets: ```yaml # 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 registry_htpasswd: "user:$2y$05$..." ``` **Registry usage after deploy:** ```bash # 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: ```bash ansible-playbook -i ansible/inventory.ini ansible/site.yml --ask-vault-pass ``` ### 5. Run the Ansible master playbook From the repo root: ```bash 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: ```bash ansible-playbook -i ansible/inventory.ini ansible/site.yml --tags ```