Move stages, runner and deploy target to dedicated host ONE
All checks were successful
CD / Build and push images (push) Successful in 3m45s
CI / Lint, typecheck, test (push) Successful in 3m39s
CI / Auth e2e pack (push) Successful in 3m43s
CI / Import/export fidelity gate (push) Successful in 43s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m12s
CD / Promote to Int (push) Successful in 10s

Gitea itself relocated to the dedicated Hetzner server ONE
(one.101010.cloud); this commit moves the rest of the delivery chain
along: cd.yml now deploys to one.101010.cloud (DEPLOY_HOST_KEY secret
rotated to ONE's host key), the act_runner one-dorfteich replaces
vps-dorfteich, and the stage/deployment docs describe the new topology
(Test/Int migrated with volumes; Prod joins on the same host at M8).
The Int promotion gate variable RUN_INT_DEPLOY was removed — the new
host has the capacity, so promote-int deploys again by default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
This commit is contained in:
Claude Fable 5 2026-07-11 08:05:36 +02:00
parent f3938b7fdb
commit 514cdf5ead
6 changed files with 49 additions and 35 deletions

View File

@ -11,7 +11,7 @@ on:
env: env:
IMAGE_BASE: gitea.101010.cloud/stwaidele/dorfteich IMAGE_BASE: gitea.101010.cloud/stwaidele/dorfteich
DEPLOY_HOST: 188.245.116.44 DEPLOY_HOST: one.101010.cloud
jobs: jobs:
build-push: build-push:
@ -105,12 +105,9 @@ jobs:
needs: smoke-test needs: smoke-test
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Toggle: set the repo/org Actions variable `RUN_INT_DEPLOY` to `false` # Toggle: set the repo/org Actions variable `RUN_INT_DEPLOY` to `false`
# (Gitea → Settings → Actions → Variables) to stop redeploying the Int stack # (Gitea → Settings → Actions → Variables) to skip redeploying the Int
# while the runner host is resource-constrained; the Int stack can then stay # stack (e.g. host maintenance); unset/anything-else runs the normal
# `docker compose down`. Unset/anything-else keeps the normal promotion, so # promotion. Int is a preview stage; CI quality gates are unaffected.
# this defaults to the previous behaviour and re-enabling is a variable flip
# (no code change). Int is a preview stage; the CI quality gates are
# unaffected either way.
if: ${{ vars.RUN_INT_DEPLOY != 'false' }} if: ${{ vars.RUN_INT_DEPLOY != 'false' }}
steps: steps:
- name: Log in to the Gitea registry - name: Log in to the Gitea registry

View File

@ -18,7 +18,7 @@ IMAGE_PREFIX=dorfteich
TAG=latest TAG=latest
# --- ports (localhost only; the host reverse proxy routes to these) --------- # --- ports (localhost only; the host reverse proxy routes to these) ---------
# Suggested per stage on the shared VPS: test 8100/8101/8102, # Suggested per stage on the shared host (ONE): test 8100/8101/8102,
# int 8110/8111/8112, prod 8120/8121/8122 (web/api/collab). # int 8110/8111/8112, prod 8120/8121/8122 (web/api/collab).
WEB_PORT=8100 WEB_PORT=8100
API_PORT=8101 API_PORT=8101

View File

@ -1,7 +1,9 @@
# Stage provisioning on the VPS (188.245.116.44) # Stage provisioning on the dedicated host ONE (one.101010.cloud)
Test and Int run as Compose stacks on the operator's VPS; DNS for Test and Int run as Compose stacks on the operator's dedicated Hetzner
`*.dorfteich.cloud` already points there (deployment.md §Stages). Steps server ONE (`one.101010.cloud`, 168.119.32.247) — the same host that runs
the Gitea instance and the CI runner; DNS for `*.dorfteich.cloud` already
points there (deployment.md §Stages). Steps
marked **[root]** need host root access and are executed by the repo marked **[root]** need host root access and are executed by the repo
owner; everything else can be done by CI or a deploy user. owner; everything else can be done by CI or a deploy user.
@ -54,7 +56,7 @@ FIXTURE_ADMIN_PASSWORD=… FIXTURE_USER_PASSWORD=… \
``` ```
(The stage db is not published; tunnel to the db container, e.g. (The stage db is not published; tunnel to the db container, e.g.
`ssh -L 15432:<db-container-ip>:5432 root@188.245.116.44`.) `ssh -L 15432:<db-container-ip>:5432 root@one.101010.cloud`.)
## 2. Reverse proxy vhosts **[root]** ## 2. Reverse proxy vhosts **[root]**
@ -83,24 +85,25 @@ Int: same block with `int.dorfteich.cloud` and ports 8110/8111/8112.
## 3. Gitea act_runner **[root]** ## 3. Gitea act_runner **[root]**
The CI/CD workflows (`.gitea/workflows/`) need one act_runner on the VPS The CI/CD workflows (`.gitea/workflows/`) need one act_runner on the host
with Docker access and the `ubuntu-latest` label: with Docker access and the `ubuntu-latest` label:
```sh ```sh
# 1. Download act_runner (https://gitea.com/gitea/act_runner/releases) # 1. Download act_runner (https://gitea.com/gitea/act_runner/releases)
# 2. Registration token: Gitea → Site/Repo Settings → Actions → Runners # 2. Registration token: Gitea → Site/Repo Settings → Actions → Runners
# (or: docker exec -u git gitea_app gitea actions generate-runner-token)
act_runner register \ act_runner register \
--instance https://gitea.101010.cloud \ --instance https://gitea.101010.cloud \
--token <REGISTRATION_TOKEN> \ --token <REGISTRATION_TOKEN> \
--name vps-dorfteich \ --name one-dorfteich \
--labels ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-22.04 --labels ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest
# 3. Run as a systemd service (act_runner daemon), user in the docker group. # 3. Run as a systemd service (act_runner daemon), user in the docker group.
``` ```
## 4. Deploy user and SSH keys ## 4. Deploy user and SSH keys
The CD workflow (issue #8) deploys via SSH: The CD workflow (issue #8) deploys via SSH:
`ssh deploy@188.245.116.44 'cd /home/DOCKER/dorfteich-test && docker compose pull && docker compose up -d'`. `ssh deploy@one.101010.cloud 'cd /home/DOCKER/dorfteich-test && docker compose pull && docker compose up -d'`.
- **[root]** Create a `deploy` user (or reuse an existing deployment - **[root]** Create a `deploy` user (or reuse an existing deployment
user), member of the `docker` group, owning the stage directories. user), member of the `docker` group, owning the stage directories.
@ -116,7 +119,7 @@ The pipeline pushes images to the Gitea container registry
- Repository secret `REGISTRY_TOKEN`: a Gitea access token with - Repository secret `REGISTRY_TOKEN`: a Gitea access token with
`write:package` scope (owner `stwaidele` or a CI account). `write:package` scope (owner `stwaidele` or a CI account).
- On the VPS, `docker login gitea.101010.cloud` for the `deploy` user - On the host, `docker login gitea.101010.cloud` for the `deploy` user
with a `read:package` token, so `compose pull` works. with a `read:package` token, so `compose pull` works.
## 6. Verification checklist ## 6. Verification checklist
@ -133,5 +136,14 @@ The pipeline pushes images to the Gitea container registry
- 2026-07-05: Test/Int stage directories, `.env` files, Caddy vhosts - 2026-07-05: Test/Int stage directories, `.env` files, Caddy vhosts
(TLS live), `deploy` user, act_runner (v0.6.1, systemd) and registry (TLS live), `deploy` user, act_runner (v0.6.1, systemd) and registry
login provisioned; Gitea Actions enabled instance-wide (`app.ini` on login provisioned on the shared 4-GB VPS (188.245.116.44); Gitea
BASEL, backup kept). First pipeline run = this commit. Actions enabled instance-wide (`app.ini` on BASEL, backup kept).
First pipeline run = this commit.
- 2026-07-11: **Everything moved to the dedicated host ONE**
(`one.101010.cloud`, 168.119.32.247) after Gitea itself relocated
there: stage volumes (`db-data`, `uploads`) and `.env` files copied
1:1, compose files refreshed from the repo (now includes the `plugins`
volume from #71), Caddy vhosts recreated (prod block prepared but
commented out), act_runner `one-dorfteich` registered, old VPS runner
and stacks stopped (kept as rollback reserve). `DEPLOY_HOST` in
`cd.yml` and the `DEPLOY_HOST_KEY` secret updated accordingly.

View File

@ -7,17 +7,19 @@
The project is hosted on a self-managed Gitea (`gitea.101010.cloud`). The project is hosted on a self-managed Gitea (`gitea.101010.cloud`).
Environments (kickoff): Dev runs locally on contributors' machines; Environments (kickoff): Dev runs locally on contributors' machines;
Test and Int run as separate Compose stacks on the operator's VPS Test and Int run as separate Compose stacks on the operator's dedicated
(`188.245.116.44`, domains `test.dorfteich.cloud` / `int.dorfteich.cloud`); server ONE (`one.101010.cloud`, domains `test.dorfteich.cloud` /
the Prod host (`dorfteich.online`) is decided at go-live — the VPS or a `int.dorfteich.cloud`), which also hosts Gitea itself; Prod
dedicated host — so the architecture must keep that choice and any later (`dorfteich.online`) goes live on the same host at M8 — the architecture
move cheap. Contributors include AI coding sessions — gates must be must keep any later move cheap. Contributors include AI coding sessions —
automated and objective wherever possible. gates must be automated and objective wherever possible.
(Originally the stages and runner lived on a shared 4-GB VPS,
`188.245.116.44`; everything moved to ONE on 2026-07-11.)
## Decision ## Decision
- **Gitea Actions** is the CI/CD system (GitHub-Actions-compatible syntax); - **Gitea Actions** is the CI/CD system (GitHub-Actions-compatible syntax);
an act_runner runs on the VPS (`188.245.116.44`) with Docker access. an act_runner runs on ONE (`one.101010.cloud`) with Docker access.
- **Images** are built once per change and promoted, never rebuilt per - **Images** are built once per change and promoted, never rebuilt per
stage: pushed to the **Gitea container registry** stage: pushed to the **Gitea container registry**
(`gitea.101010.cloud/stwaidele/dorfteich-{web,api,collab}`), tagged with (`gitea.101010.cloud/stwaidele/dorfteich-{web,api,collab}`), tagged with

View File

@ -2,10 +2,12 @@
Four stages, one Compose definition. Foundational decisions: ADR 0014 Four stages, one Compose definition. Foundational decisions: ADR 0014
(CI/CD), ADR 0015 (backup), kickoff topology decision (Dev local on the (CI/CD), ADR 0015 (backup), kickoff topology decision (Dev local on the
developer's machine; Test and Int on the operator's VPS `188.245.116.44`; developer's machine; Test, Int and — from M8 — Prod on the operator's
the Prod host is decided at go-live — the architecture must keep that dedicated Hetzner server ONE (`one.101010.cloud`, 168.119.32.247), which
choice and any later move cheap). DNS status: `*.dorfteich.online` and also hosts the Gitea instance and the CI runner; the architecture must
`*.dorfteich.cloud` already point to the VPS. keep any later move cheap). DNS status: `*.dorfteich.online` and
`*.dorfteich.cloud` already point to ONE. (Until 2026-07-11 the stages
ran on a shared 4-GB VPS, `188.245.116.44`.)
## The Compose stack ## The Compose stack
@ -47,9 +49,9 @@ Self-hosters without a proxy can enable the optional `caddy` Compose profile
| Stage | Where | Domain | Purpose | Data | | Stage | Where | Domain | Purpose | Data |
| -------- | ----------------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------- | ------------------------------- | | -------- | ----------------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------- | ------------------------------- |
| **Dev** | contributor machine (e.g. the operator's MacBook), Docker Desktop | `localhost` | feature work; hot reload via `compose.dev.yml` overlay (source mounts, vite dev server) | fixtures/seed script | | **Dev** | contributor machine (e.g. the operator's MacBook), Docker Desktop | `localhost` | feature work; hot reload via `compose.dev.yml` overlay (source mounts, vite dev server) | fixtures/seed script |
| **Test** | VPS `188.245.116.44`, `/home/DOCKER/dorfteich-test/` | `test.dorfteich.cloud` | auto-deploy target of `main`; e2e suite runs here | reset-able; seeded | | **Test** | ONE `one.101010.cloud`, `/home/DOCKER/dorfteich-test/` | `test.dorfteich.cloud` | auto-deploy target of `main`; e2e suite runs here | reset-able; seeded |
| **Int** | VPS `188.245.116.44`, `/home/DOCKER/dorfteich-int/` | `int.dorfteich.cloud` | stable preview; manual/exploratory testing; release candidates | persistent test data | | **Int** | ONE `one.101010.cloud`, `/home/DOCKER/dorfteich-int/` | `int.dorfteich.cloud` | stable preview; manual/exploratory testing; release candidates | persistent test data |
| **Prod** | host decided at go-live (M8): the VPS or a dedicated host | `dorfteich.online` | public flagship instance | real data; full backup + mirror | | **Prod** | ONE `one.101010.cloud`, `/home/DOCKER/dorfteich-prod/` (M8, #89) | `dorfteich.online` | public flagship instance | real data; full backup + mirror |
Stage layout follows the operator's Docker host convention: Stage layout follows the operator's Docker host convention:
compose file + `.env` under `/home/DOCKER/dorfteich-<stage>/`, bulk data compose file + `.env` under `/home/DOCKER/dorfteich-<stage>/`, bulk data

View File

@ -14,8 +14,9 @@ Goal: empty but deployed. Monorepo scaffold (pnpm, ESLint/Prettier, Vitest,
Playwright), NestJS api with `/healthz` + Prisma + first migration, React Playwright), NestJS api with `/healthz` + Prisma + first migration, React
SPA shell with routing + i18n scaffold (ADR 0012), Dockerfiles + Compose SPA shell with routing + i18n scaffold (ADR 0012), Dockerfiles + Compose
stack + dev overlay, Gitea Actions pipeline (lint/test/build → images → stack + dev overlay, Gitea Actions pipeline (lint/test/build → images →
deploy Test → e2e smoke → promote Int), stage setup on the VPS deploy Test → e2e smoke → promote Int), stage setup on the operator's
(`188.245.116.44`). host (originally the VPS `188.245.116.44`; since 2026-07-11 the dedicated
server ONE, `one.101010.cloud`).
**Exit criterion**: a commit to `main` automatically reaches **Exit criterion**: a commit to `main` automatically reaches
`test.dorfteich.cloud` and shows a styled "hello" shell. `test.dorfteich.cloud` and shows a styled "hello" shell.