Files
coa-server-manager/docs/INFRASTRUCTURE.md
T
ДмитрийandClaude Sonnet 5.5 0527e045ba docs: VPS infrastructure baseline (Phase -1)
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>
2026-10-06 06:40:50 +05:00

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 the coa-db internal 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 reboot and 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):
    1. PostgreSQL logical backup: pg_dump to /opt/coa/backups, copied off the node.
    2. Infrastructure config backup: /opt/coa except secrets/ and volumes.
    3. Secrets are backed up separately and never together with the config archive.
  • 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.