Repository navigation
Platform Deploy
Patrick Stähle edited this page Oct 30, 2026
·
1 revision
Snapshot of
platform/deploy/README.mdat commit707b4a0, preserved before the docs were compacted. History only: the current specs are in the repository.
One server runs the API, Postgres and Caddy (HTTPS) with Docker Compose
(compose.yaml). CI builds the image ghcr.io/runningjoke/reactpod-platform
and deploys over SSH (.github/workflows/platform.yml).
The deploy job is skipped until the secrets below exist.
- Server: Hetzner Cloud, e.g. CX22 (2 vCPU, 4 GB), location Falkenstein or Nuremberg (EU), image Ubuntu 24.04, your SSH key. Enable backups in the Hetzner console.
-
DNS: an
A(andAAAA) record, e.g.api.staging.<your-domain>→ server IP. -
Firewall (Hetzner firewall or
ufw): allow 22, 80, 443 (TCP) and 443 (UDP), nothing else. -
Docker:
curl -fsSL https://get.docker.com | sh. -
Deploy user (no root logins from CI):
adduser --disabled-password deploy && usermod -aG docker deploy mkdir -p /opt/reactpod && chown deploy: /opt/reactpod # on your machine: ssh-keygen -t ed25519 -f reactpod-deploy -N "" # put reactpod-deploy.pub into /home/deploy/.ssh/authorized_keys
-
Configuration:
/opt/reactpod/.envfrom .env.example with a long randomPOSTGRES_PASSWORD(openssl rand -base64 32) and yourAPI_DOMAIN.chmod 600 .env. Mail: sign-in codes and invitations go out by SMTP (MAIL_URL,MAIL_FROM). Pick a provider with EU processing and a data processing agreement, and set up SPF/DKIM for the sender domain. WithoutMAIL_URLthe mails only appear indocker compose logs api(fine for a first staging test, not for real users). -
Registry access: while the package is private, log in once as
deploy:echo <PAT with read:packages> | docker login ghcr.io -u <github-user> --password-stdin(or make the package public under GitHub → Packages → reactpod-platform → settings).
Repository → Settings → Environments → staging:
| Name | Kind | Value |
|---|---|---|
STAGING_HOST |
secret | server IP or host name |
STAGING_USER |
secret | deploy |
STAGING_SSH_KEY |
secret | content of the private key reactpod-deploy
|
STAGING_DOMAIN |
variable |
api.staging.<your-domain> (checked after the deploy) |
- Copies
compose.yamlandCaddyfileto/opt/reactpod/. API_TAG=sha-<commit> docker compose pull-
docker compose run --rm migrate(Drizzle migrations from the image) docker compose up -d --remove-orphans- CI checks
https://$STAGING_DOMAIN/v1/meta.
- Logs:
docker compose logs -f apiin/opt/reactpod. -
Roll back:
API_TAG=sha-<older commit> docker compose up -d(image tags stay in ghcr). Migrations are forward-only; a release that changes tables must stay compatible with the previous image for one deploy. -
Database backup (besides Hetzner server backups), daily via cron as
deploy:docker compose exec -T db pg_dump -U reactpod reactpod | gzip > /opt/reactpod/backup/$(date +%F).sql.gzand copy off the server (e.g. Hetzner Storage Box). Keep 14 days. - Production later: the same files on a second server (or a managed EU Postgres via
DATABASE_URL), its own environmentproduction, deployed fromplatform-v*tags.
For users and coaches
Repository and workflow
Firmware
- Firmware Overview
- Firmware Architecture
- Firmware Link Protocol
- Firmware Config API
- Firmware Programs
- Firmware Testing
- Firmware Timing
- Firmware Host Syntax Check
Config App
Community platform (on hold)
Decisions (ADRs)
- Decisions
- ADR 0001 Repo Layout and Workflow
- ADR 0002 Versions and Firmware Delivery
- ADR 0003 Community Platform
- ADR 0004 Platform Architecture
- ADR 0005 Live Results over BLE
- ADR 0006 No OTA
- ADR 0007 Platform on Hold
Archive