Record how the OVHcloud VPS used for the future registry/relay was set up: hardening, firewall policy, Docker layout, health check, recovery path. No IP addresses, keys or credentials are stored. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
5.4 KiB
Infrastructure
Operating notes for the CoA infrastructure node. Current measured state: VPS_BASELINE.md.
This file never contains private keys, passwords, DB credentials, transfer PINs, API secrets or raw tokens.
<VPS_IP> stands for the public IPv4, which is intentionally not stored in git.
Role
coa-infra-01 (OVHcloud VPS, Ubuntu 24.04 LTS) is the shared infrastructure node for the planned services:
Registry API Relay Coordinator PostgreSQL Caddy (ingress) monitoring
Phase -1 only prepared the host. No Registry, Relay, Coordinator or schema is deployed. The VPS is not the source of truth for any player data.
No domain is registered. Until one exists, services are addressed by IP, and Caddy serves plain HTTP on :80.
The design keeps client protocols independent of hostnames so the relay can later be moved to separate nodes and
DNS names (api.<domain>, relay.<domain>) added without changing the client protocol.
Service architecture
Internet
|
22/tcp -> sshd
80,443/tcp
v
Caddy (container, network coa-ingress)
|
+--> Registry API (future, coa-ingress)
|
+--> PostgreSQL (future, network coa-db, --internal, no published port)
Rules:
- PostgreSQL must never be internet-exposed: no
ports:entry, only on thecoa-dbinternal network, no UFW rule for 5432. - Caddy is the only container allowed to publish ports (80, 443/tcp). A new inbound port needs a specific protocol and a threat model first, then a UFW rule and a note here.
- No admin dashboards or the Docker API on public interfaces. Caddy's admin API is disabled.
Public ports
| Port | Proto | Service |
|---|---|---|
| 22 | tcp | SSH, key only |
| 80 | tcp | Caddy |
| 443 | tcp | Caddy |
Filesystem layout
/opt/coa/
├── bin/ helper scripts (health.sh)
├── caddy/ compose.yaml, Caddyfile
├── postgres/ compose.yaml (prepared, not started)
├── registry/ (empty)
├── relay/ (empty)
├── coordinator/ (empty)
├── monitoring/ (empty)
├── backups/ 0700, logical dumps and config archives
├── logs/
└── secrets/ 0700 root:root, never in git
Config under /opt/coa/* is owned by ubuntu. Secrets live only in /opt/coa/secrets and are referenced from
compose files as Docker secrets/files, never inline.
SSH procedure
ssh ubuntu@<VPS_IP>
- ED25519 key only; root login and password authentication are disabled (
/etc/ssh/sshd_config.d/00-coa-hardening.conf). - To change SSH settings: edit the drop-in, run
sudo sshd -t,sudo systemctl reload ssh, and test from a second terminal before closing the first one. - Lost key: use the OVH KVM console (or reinstall the VPS with a new key in the OVH reinstall dialog; this wipes data).
Firewall policy
UFW, default deny incoming. Currently allowed: OpenSSH, 80/tcp, 443/tcp.
sudo ufw status verbose
Docker-published ports bypass UFW (see VPS_BASELINE.md). Check sudo docker ps and sudo ss -tulnH after every
deployment; the only public listeners must be 22, 80, 443.
fail2ban protects SSH (default sshd jail):
sudo fail2ban-client status sshd
Update procedure
-
Security updates install automatically (unattended-upgrades); no automatic reboot and no release upgrades.
-
Manual full update (monthly, or after security advisories):
sudo apt update && sudo apt full-upgrade -y test -f /var/run/reboot-required && echo "reboot needed"Reboot with
sudo systemctl rebootand verify with/opt/coa/bin/health.sh. -
Containers: pin image tags, then
cd /opt/coa/<service> && sudo docker compose pull && sudo docker compose up -d. -
Docker Engine updates come through the normal apt repository.
Health check
/opt/coa/bin/health.sh
Shows uptime/load, disk, RAM, listening sockets, failed systemd units, UFW rules, fail2ban SSH bans and running containers. No monitoring stack is installed on purpose (no Grafana/Prometheus until there is a real need).
Deployment commands
# Caddy (running)
cd /opt/coa/caddy && sudo docker compose up -d
sudo docker compose logs --tail 50
# PostgreSQL (prepared; do NOT start until a password file exists and the schema phase begins)
sudo install -m 600 /dev/null /opt/coa/secrets/postgres_password # then write the password into it (as root)
cd /opt/coa/postgres && sudo docker compose up -d
Backups
- Provider backup is not a substitute for application-level backups. What OVH actually includes for this plan (automatic backup, snapshot, KVM/rescue) still has to be confirmed in the Control Panel.
- Planned (not implemented yet):
- PostgreSQL logical backup:
pg_dumpto/opt/coa/backups, copied off the node. - Infrastructure config backup:
/opt/coaexceptsecrets/and volumes. - Secrets are backed up separately and never together with the config archive.
- PostgreSQL logical backup:
- Portable Characters are not backed up here: the VPS is not their source of truth.
Recovery
| Situation | Path |
|---|---|
| SSH lockout / bad firewall | OVH Control Panel -> VPS -> KVM console, log in locally, fix ufw/sshd drop-in |
| Broken OS | OVH rescue mode, or Reinstall (Ubuntu 24.04 + pre-installed ED25519 key), then re-apply this document |
| Lost VPS | New VPS, follow VPS_BASELINE.md "What was configured", restore config archive and PostgreSQL dump |
Reinstall destroys all data on the VPS.