The hosting and CI/CD environment: version control, continuous integration and web hosting, orchestrated by Docker Compose.
| Service | Image | Role |
|---|---|---|
| Gitea | gitea/gitea:1.27.3 |
Self-hosted Git service; its own stack since 2026-10-23. |
| Woodpecker Server | woodpeckerci/woodpecker-server:v3.19.0 |
CI/CD server, integrated with Gitea; its own stack since 2026-10-23. |
| Woodpecker Agent | woodpeckerci/woodpecker-agent:v3.19.0 |
Runs the pipeline steps in containers through the Docker socket, up to 3 workflows at a time. |
| MySQL | mysql:8 |
The softwares database: software metadata and releases. |
| API | local build | Rails 8 app: catalog REST API, file downloads, ActiveAdmin, /build/* endpoints. Routed for /api, /file, /build and /admin on teletypegames.org. |
| Frontend | local build | The Vue 3 / Vite game portal. |
Artifacts arrive over HTTP: POST /build/upload writes into the API's artifact directory — ./data/softwares on the host, mounted as /softwares in the container (FILE_CONTAINER_PATH). The updater and /file/* static serving read the same directory.
A game repo's .woodpecker.yaml is a one-line marker (platform: <platform>, plus name: when the software name differs from the repo name). The full pipeline comes from the update server's Woodpecker configuration extension — see WarpEngine:
curl "https://teletypegames.org/build/config?platform=<platform>"
The repo also needs metadata.json and the application_token Woodpecker secret. After building, the pipeline publishes in two steps.
1. Upload — one multipart curl request per file. Filenames must carry the <name>-<version> prefix.
curl -X POST -H "X-Update-Secret: ${UPDATE_SECRET}" -F "file=@<name>-<version>.zip" \
"${UPDATE_SERVER}/build/upload?name=...&version=..."
2. Publish — registers the new version.
curl -X POST -H "X-Update-Secret: ${UPDATE_SECRET}" \
"${UPDATE_SERVER}/build/publish?platform=...&name=...&version=..."
UPDATE_SECRET is a WarpEngine::ApplicationToken with the update + upload scopes — per-owner, optionally expiring, created in the admin.
Once published, the software and its releases appear on the teletypegames.org portal automatically.
The services on vps.teletype.hu deploy from Woodpecker too, but without host volumes or the Docker socket in the pipeline. The deploy step only connects to deploy@vps.teletype.hu with the repo's own key (secret deploy_ssh_key). On the server that key is bound to one fixed command, ci-deploy <target>, and the target's config in host-config (/etc/ci-deploy/<target>.conf) says what happens. The caller can pass nothing.
| Target type | What the server does | Used for |
|---|---|---|
compose-git |
clones master from Gitea itself, checks compose.yaml against the policy, copies the listed paths into the stack, docker compose up -d --build |
public repos: FTP server, LLM, Teletype BBS, Impostor BBS, Alexandria BBS, Feedia BBS, BBS Web, Statusbot, Updater, TTGWIKI exporter |
compose-checkout |
fast-forwards the server's own git checkout from Gitea (read-only deploy key), policy check, docker compose up -d --build |
private repos: Redmine, Matomo, ResourceSpace; the wiki (there the wiki-fetcher syncs) |
compose-services |
builds and replaces only the named services of a git checkout, runs migrations, health check | teletypegames.org |
git-publish |
static content | presentation |
The DOS store (the FTP server's APPS) has no deploy targets since WarpEngine 1.7: it is the WarpEngine's DOS shelf, rebuilt from the catalog on every publish.
The policy (ci-deploy-compose-policy) 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 /var/log/nginx) 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:
compose-git'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 VOLUME), copy the files into the image too, so a change rebuilds it and the container is recreated..env, db.env); the pipeline's docker compose config check runs with a dummy one.Stacks that are deployed by hand on purpose: Gitea and Woodpecker (CI must not deploy itself), 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).