diff --git a/playbooks/planka.md b/playbooks/planka.md new file mode 100644 index 0000000..d7c8818 --- /dev/null +++ b/playbooks/planka.md @@ -0,0 +1,105 @@ +# Planka Kanban Board Playbook + +## Overview + +`planka.yml` deploys [Planka](https://planka.app), a self-hosted kanban board application. The playbook provisions a Postgres database and the Planka app container via Docker Compose, generates and persists cryptographic secrets, and registers the stack on the `pangolin` overlay network for reverse-proxy access. + +## Architecture + +``` +┌─────────────────────────────────┐ ┌──────────────────────────┐ +│ Target Docker Host │ │ Pangolin Server │ +│ │ │ pangolin.wulfconsulting │ +│ ┌─────────────┐ planka_net │ │ .cloud │ +│ │ planka_app │◀──────────────▶│ │ │ +│ │ (planka) │ :3000→1337 │ pangolin│ Routes traffic to │ +│ └──────┬──────┘ network ─────────────▶│ planka. │ +│ │ │ └──────────────────────────┘ +│ ┌──────▼──────┐ │ +│ │ planka- │ │ +│ │ postgres │ │ +│ │ (pg 16) │ │ +│ └─────────────┘ │ +│ │ +│ /opt/stacks/planka/ │ +│ ├── compose.yaml │ +│ └── .secrets (mode 0600) │ +└─────────────────────────────────┘ +``` + +### Playbook structure + +| Section | Purpose | +|---------|---------| +| **vars** | Sets stack directory, Planka version, port, base URL, admin credentials, and secret-rotation flag | +| **pre_tasks** | Validates admin password is not default, checks `planka_base_url` is set, verifies Docker is running, ensures `pangolin` Docker network exists | +| **tasks** | Creates stack directory, generates/persists/reads cryptographic secrets, renders `compose.yaml`, deploys via `docker_compose_v2`, health-checks the HTTP endpoint, prints a deployment summary | +| **handlers** | Restarts the Planka stack when `compose.yaml` changes | + +Secrets (`SECRET_KEY` and `INTERNAL_ACCESS_TOKEN`) are generated on first run and stored in `/opt/stacks/planka/.secrets` (root-only, mode `0600`). Subsequent runs re-use the persisted values unless `planka_rotate_secrets: true` is passed. + +## Usage + +### Required extra vars + +| Variable | Description | +|----------|-------------| +| `planka_admin_password` | Admin account password — must not be the default `ChangeMe123!` | +| `planka_base_url` | Public URL Planka will be accessed at (used in auth cookies and links) | + +### Optional extra vars + +| Variable | Default | Description | +|----------|---------|-------------| +| `planka_version` | `2.0.0-rc.3` | Planka image tag | +| `planka_port` | `3000` | Host port mapped to the Planka container | +| `planka_trust_proxy` | `false` | Set `true` when behind a reverse proxy (enables real-IP headers) | +| `planka_token_expires_in` | `365` | Session token lifetime in days | +| `planka_log_level` | `warn` | Planka log verbosity (`debug`, `info`, `warn`, `error`) | +| `planka_db_name` | `planka` | Postgres database name | +| `planka_admin_email` | `admin@example.com` | Admin account e-mail | +| `planka_admin_name` | `Planka Admin` | Admin display name | +| `planka_admin_username` | `admin` | Admin username | +| `planka_rotate_secrets` | `false` | Set `true` to force regeneration of `SECRET_KEY` and `INTERNAL_ACCESS_TOKEN` | + +### Deploy to a host + +```bash +ansible-playbook playbooks/planka.yml --limit myhost \ + -e planka_admin_password=SuperSecret99! \ + -e planka_base_url=https://planka.wulfconsulting.cloud +``` + +### Override image version and port + +```bash +ansible-playbook playbooks/planka.yml --limit myhost \ + -e planka_admin_password=SuperSecret99! \ + -e planka_base_url=https://planka.wulfconsulting.cloud \ + -e planka_version=latest \ + -e planka_port=3001 +``` + +### Rotate secrets on an existing deployment + +```bash +ansible-playbook playbooks/planka.yml --limit myhost \ + -e planka_admin_password=SuperSecret99! \ + -e planka_base_url=https://planka.wulfconsulting.cloud \ + -e planka_rotate_secrets=true +``` + +### Verify the containers are running + +```bash +ansible myhost -a "docker ps --filter name=planka" +``` + +## Notes + +- **`planka_base_url` is required.** Planka embeds this URL in auth tokens and e-mail links; an incorrect value will break logins from behind a proxy. +- **`planka_trust_proxy: true` is needed behind Pangolin/Traefik.** Without it, Planka sees the proxy's IP instead of the real client IP. +- **Admin credentials are first-run only.** `DEFAULT_ADMIN_*` env vars only take effect when Planka initialises a fresh database. Changing them after the first deploy has no effect; use the Planka UI instead. +- **Secrets are host-side persistent.** `/opt/stacks/planka/.secrets` survives container re-creation. Use `planka_rotate_secrets=true` only when intentionally invalidating all active sessions. +- **`--limit` is required.** The playbook targets `all` hosts; always scope it to the intended host. +- **Postgres data is in a named Docker volume** (`db-data`). Destroying the stack with `docker compose down -v` will wipe the database.