75a0f21f74
- 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.
158 lines
4.0 KiB
Markdown
158 lines
4.0 KiB
Markdown
# 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'
|
|
<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:
|
|
|
|
```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 <user> <password>
|
|
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 <role>
|
|
```
|