TITLE=Deployment ROUTE=/infrastructure/deployment LOCALE=en MODIFIED=1792749862 URL=https://wiki.teletypegames.org/infrastructure/deployment L=1 #22 L=2 https://teletypegames.org L=3 https://git.teletypegames.org/infra/host-config L=4 #50 L=5 #73 L=6 #66 L=7 #67 L=8 #69 L=9 #71 L=10 #49 L=11 #33 L=12 #72 L=13 #68 L=14 #18 L=15 #16 L=16 #15 q│ The hosting and CI/CD environment: version control, continuous integration q│ and web hosting, orchestrated by Docker Compose. 1Components 1══════════ t┌───────────────────┬───────────────────────────┬────────────────────────────┐ t│h Service t│h Image t│h Role t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bTraefik0 t│0 ctraefik:latest0 t│0 Edge router. Host-header t│ t│0 t│0 t│0 routing, ports 80/8080. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bGitea0 t│0 cgitea/gitea:latest0 t│0 Self-hosted Git service. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bWoodpecker Server0 t│0 cwoodpeckerci/woodpecker-0 t│0 CI/CD server, integrated t│ t│0 t│0 cserver0 t│0 with Gitea. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bWoodpecker Agent0 t│0 cwoodpeckerci/woodpecker-0 t│0 Runs the pipeline steps; t│ t│0 t│0 cagent0 t│0 mounts the Docker socket t│ t│0 t│0 t│0 and the shared csoftwares0 t│ t│0 t│0 t│0 volume. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bMySQL0 t│0 cmysql:80 t│0 The csoftwares0 database: t│ t│0 t│0 t│0 software metadata and t│ t│0 t│0 t│0 releases. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bphpMyAdmin0 t│0 cphpmyadmin:latest0 t│0 Web UI for the database. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bAPI0 t│0 local build t│0 Rails 8 app: catalog REST t│ t│0 t│0 t│0 API, file downloads, t│ t│0 t│0 t│0 ActiveAdmin, c/build/*0 t│ t│0 t│0 t│0 endpoints. Routed for t│ t│0 t│0 t│0 c/api0, c/file0, c/build0 and t│ t│0 t│0 t│0 c/admin0 on t│ t│0 t│0 t│0 cteletypegames.org0. t│ t├───────────────────┼───────────────────────────┼────────────────────────────┤ t│0 bFrontend0 t│0 local build t│0 The Vue 3 / Vite game t│ t│0 t│0 t│0 portal. t│ t└───────────────────┴───────────────────────────┴────────────────────────────┘ 1Shared storage 1══════════════ Artifacts arrive over HTTP: cPOST /build/upload0 writes into the API's artifact directory — c./data/softwares0 on the host, mounted as c/softwares0 in the container (cFILE_CONTAINER_PATH0). The updater and c/file/*0 static serving read the same directory. 1Deployment workflow 1═══════════════════ A game repo's c.woodpecker.yaml0 is a one-line marker (cplatform: 0, plus cname:0 when the software name differs from the repo name). The full pipeline comes from the update server's Woodpecker configuration extension — see 1WarpEngine: p curl "https://teletypegames.org/build/config?platform=" The repo also needs cmetadata.json0 and the capplication_token0 Woodpecker secret. After building, the pipeline publishes in two steps. b1. Upload0 — one multipart ccurl0 request per file. Filenames must carry the c-0 prefix. p curl -X POST -H "X-Update-Secret: ${UPDATE_SECRET}" -F "file=@-.zip" \ p "${UPDATE_SERVER}/build/upload?name=...&version=..." b2. Publish0 — registers the new version. p curl -X POST -H "X-Update-Secret: ${UPDATE_SECRET}" \ p "${UPDATE_SERVER}/build/publish?platform=...&name=...&version=..." cUPDATE_SECRET0 is a cWarpEngine::ApplicationToken0 with the cupdate0 + cupload scopes — per-owner, optionally expiring, created in the admin. Once published, the software and its releases appear on the 2teletypegames.org portal automatically. 1Server stacks (ci-deploy) 1═════════════════════════ The services on vps.teletype.hu deploy from Woodpecker too, but without host volumes or the Docker socket in the pipeline. The cdeploy0 step only connects to cdeploy@vps.teletype.hu0 with the repo's own key (secret cdeploy_ssh_key0). On the server that key is bound to one fixed command, cci-deploy 0, and the target's config in 3host-config (c/etc/ci-deploy/.conf0) says what happens. The caller can pass nothing. t┌────────────────────────┬─────────────────────────┬─────────────────────────┐ t│h Target type t│h What the server does t│h Used for t│ t├────────────────────────┼─────────────────────────┼─────────────────────────┤ t│0 ccompose-git0 t│0 clones master from t│0 public repos: 4FTP t│ t│0 t│0 Gitea itself, checks t│0 4server, 5LLM, 6Teletype t│ t│0 t│0 ccompose.yaml0 against t│0 6BBS, Impostor BBS, 7BBS t│ t│0 t│0 the policy, copies the t│0 7Web, 8Statusbot, t│ t│0 t│0 listed paths into the t│0 9Updater, 10TTGWIKI t│ t│0 t│0 stack, cdocker compose0 t│0 exporter t│ t│0 t│0 cup -d --build0 t│0 t│ t├────────────────────────┼─────────────────────────┼─────────────────────────┤ t│0 ccompose-checkout0 t│0 fast-forwards the t│0 private repos: Redmine, t│ t│0 t│0 server's own git t│0 Matomo, 11ResourceSpace; t│ t│0 t│0 checkout from Gitea t│0 the 12wiki (there the t│ t│0 t│0 (read-only deploy key), t│0 wiki-fetcher syncs) t│ t│0 t│0 policy check, cdocker0 t│0 t│ t│0 t│0 ccompose up -d --build0 t│0 t│ t├────────────────────────┼─────────────────────────┼─────────────────────────┤ t│0 ccompose-services0 t│0 builds and replaces t│0 13teletypegames.org t│ t│0 t│0 only the named services t│0 t│ t│0 t│0 of a git checkout, runs t│0 t│ t│0 t│0 migrations, health t│0 t│ t│0 t│0 check t│0 t│ t├────────────────────────┼─────────────────────────┼─────────────────────────┤ t│0 cgit-publish0, t│0 static content, and the t│0 presentation, 14GMENU, t│ t│0 cstore-upload0 t│0 DOS store builds into t│0 15DOSINST, 16BMAGIC t│ t│0 t│0 the FTP server's cAPPS0 t│0 t│ t└────────────────────────┴─────────────────────────┴─────────────────────────┘ The policy (cci-deploy-compose-policy0) refuses privileged mode, extra capabilities, host namespaces, devices, the Docker socket, bind mounts outside the stack (except the paths the target allows, e.g. Matomo's read-only c/var/log/nginx0) and public ports the target does not list. So a push to master changes how a stack behaves, but does not give root. Two things to know when writing a stack for it: • bThe deploy replaces files, it does not edit them.0 A single file mounted into a container keeps showing the old file after a deploy, and a directory listed in ccompose-git0's copy list is replaced as a whole. Bake such files into the image, or mount the folder they are in. Where a mount has to stay (e.g. under an image's cVOLUME0), copy the files into the image too, so a change rebuilds it and the container is recreated. • bSecrets stay on the server0 (c.env0, cdb.env0); the pipeline's cdocker compose cconfig0 check runs with a dummy one. Stacks that are deployed by hand on purpose: Authentik (the single sign-on everything depends on) and the observability stack (it needs host-level mounts). Onboarding a new target is described in the vps-assistant notes (server documentation, section 9.20).