boma/docs/decisions/004-docker-model.md
sjat fe4228fb38 Add architecture decision records and runbooks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 14:10:01 +02:00

77 lines
3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-004 — Docker and Compose service model
## Context
All services run as Docker containers managed via Docker Compose. This document
defines how services are structured, deployed, and maintained.
## Core principles
- **No hand-edited files on hosts**: all Compose files are rendered by Ansible
from Jinja2 templates. If a file exists on a host, it was put there by Ansible.
- **Compose per service**: each service (or tightly coupled service group) gets
its own Compose file and directory under a standard path.
- **Variables drive differences**: the same template renders differently per host
via `group_vars` and `host_vars`. No host-specific templates.
## Directory layout on hosts
```
/opt/services/
├── servicename/
│ ├── docker-compose.yml # rendered by Ansible, never edited manually
│ ├── .env # rendered by Ansible from vault variables
│ └── data/ # persistent volumes (bind mounts)
│ └── ...
```
All services live under `/opt/services/`. The path is defined in
`group_vars/all/vars.yml` as `services__base_dir`.
## Compose file delivery
Each service has a corresponding Ansible role (or is managed by a shared role
with per-service variables). The role:
1. Creates `/opt/services/servicename/` directory
2. Renders `docker-compose.yml` from `templates/docker-compose.yml.j2`
3. Renders `.env` from `templates/env.j2` (pulling secrets from vault variables)
4. Runs `docker compose up -d --remove-orphans` via `ansible.builtin.command`
5. Optionally runs `docker compose pull` before up (controlled by variable)
## Docker daemon configuration
Managed by the `docker_host` role. Key settings:
- `"log-driver": "json-file"` with size limits (prevents disk exhaustion)
- `"iptables": false` — firewall managed entirely by nftables (see ADR-002)
- TCP socket disabled — Unix socket only (`/var/run/docker.sock`)
- User namespace remapping: evaluated per use case, not enabled by default
## Networking
- Each service Compose file defines its own named network(s)
- Services that need to communicate are placed on a shared named network
defined in a dedicated `docker-compose.networks.yml` (if cross-service
networking is needed on a host)
- External port publishing is explicit and matches nftables rules
## Image management
- Images are always pinned to a specific digest or tag in templates
- `latest` is never used in production Compose files
- Image updates are a deliberate operation: update the tag variable, run deploy
## Persistent data
- Bind mounts preferred over named volumes for data that must be backed up
- All bind mount paths are under `/opt/services/<name>/data/`
- Backup strategy is defined separately (not in scope of this repo)
## Decision
Docker Compose was chosen over Kubernetes/Swarm because:
- Appropriate complexity level for 25 hosts with independent service sets
- Compose files are human-readable and easily auditable
- No distributed state to manage
- Straightforward to back up and restore