Files
ems/docs/deployment-self-hosted.md
Dusan Vojacek 4bc9d86667
Some checks failed
test / smoke-test (push) Successful in 7s
deploy / deploy (push) Has been cancelled
spusteni jen pres WG
2026-04-05 00:50:57 +02:00

7.3 KiB

Self-hosted deploy: Gitea + Caddy + EMS (/opt/ems-deploy)

Runbook pro single-node Debian, Docker Compose, Gitea Actions bez Kubernetes. Doplňuje konkrétní stav serveru (ZFS, zfs storage driver, Caddy na hostu, Gitea v /opt/gitea-stack).


1. Co kde běží

Oblast Cesta / adresa Poznámka
Gitea (HTTP) https://git.vojacek.eu → Caddy → 127.0.0.1:3000 Gitea publikuje HTTP jen na loopback.
Gitea (Git SSH) git.vojacek.eu:2222 → kontejner :22 Host SSH zůstává na :22. V app.ini: START_SSH_SERVER = false (kvůli SSH v base image).
Gitea stack /opt/gitea-stack postgres, gitea, gitea-runner; vlastní docker-compose + .env.
Gitea runner stejný stack act_runner s přepsaným entrypoint/command; registrace ručně → runner/runner.json.
EMS deploy /opt/ems-deploy Oddělený Compose projekt od Gitea.
EMS checkout /opt/ems-deploy/app Git clone stejného repa jako v Gitea.
EMS Compose (runtime) /opt/ems-deploy/docker-compose.yml Kopíruje se ze app/deploy/docker-compose.yml při každém deployi (deploy.sh).
EMS secrets /opt/ems-deploy/.env Není v gitu; vzor .env.example v repu.

PostgREST v produkčním deploy/docker-compose.yml: služba naslouchá v Docker síti na :3000, bez mapování na host — frontend v kontejneru volá postgrest:3000. Tím se vyhne kolizi s Gitea na hostovém 127.0.0.1:3000. Pokud někdy přidáš ports pro ladění, použij jiný host port než 3000.


2. Architektonický verdikt

Směr (push → Actions → skript na hostu → docker compose build && up) je správný pro cíl „jeden server, žádný registry, žádný DinD“.

Tvrdá fakta / rizika

  1. Job Gitea Actions neběží na hostu, ale ve job kontejneru (Docker executor). Kroky typu run: /opt/ems-deploy/deploy.sh tedy neuvidí hostovské cesty, dokud je nepřimountuješ do job kontejneru a nepřidáš je do valid_volumes u runneru.
  2. Mount Docker socketu do jobu = plná moc nad hostovským Dockerem (ekvivalent root pro kontejnery). Je to běžný pattern u self-hosted runnerů; bezpečnost = důvěra k přístupu do repa a k labelům runneru, ne veřejný runner.
  3. docker: command not found v jobu je očekávané u defaultního image — buď použij image s docker + docker compose, nebo vůbec nevolaj docker uvnitř jobu (u tebe stačí spustit deploy.sh, který mluví s hostovským daemonem přes socket).
  4. ZFS + Docker zfs driver: snapshoty a zfs list rostou s image vrstvami; deploy.sh už dělá docker image prune -f (dangling). Hlídej místo na poolu a případně periodický docker system prune (opatrně — nesmí mazat věci, co potřebuješ).
  5. git reset --hard origin/main: rychlé a deterministické; jakýkoli lokální drift v /opt/ems-deploy/app na serveru se ztratí. Na produkční app adresář nesahat ručně — vždy přes git + deploy.
  6. Dvě síťě: ems_net vs gitea_net — služby se defaultně nevidí; pro EMS to obvykle nevadí. Integrovat Gitea DB s EMS nechceš.

3. Tok deploye (cílový)

flowchart LR
  dev[Dev laptop] -->|git push| gitea[Gitea main]
  gitea --> act[Gitea Actions]
  act --> runner[act_runner + Docker]
  runner --> job[Job container Alpine + sock + /opt mount]
  job --> sh[deploy.sh]
  sh --> git[git fetch / reset main]
  sh --> sync[install compose.yml]
  sh --> dc["docker compose build && up -d"]
  dc --> hostd[Host Docker Engine]
  hostd --> ems[EMS stack]
  1. Vývoj lokálně, merge do main, push na git.vojacek.eu:2222.
  2. Workflow .gitea/workflows/deploy.yml (label self-hosted) spustí job.
  3. Job kontejner má docker.sock a rw mount /opt/ems-deploy; nainstaluje git, bash, docker-cli, docker-cli-compose (Alpine).
  4. /opt/ems-deploy/deploy.sh: flock, git v app/, sync docker-compose.yml, docker compose config, build, up -d, image prune -f.

Build probíhá na hostovském Dockeru (stejný daemon jako Gitea stack), bez DinD.


4. Bootstrap serveru (jednorázově)

Přesně podle hlavičky v deploy/deploy.sh:

sudo mkdir -p /opt/ems-deploy/app
sudo chown -R "$USER:$USER" /opt/ems-deploy
git clone ssh://git@git.vojacek.eu:2222/vojacekd/ems.git /opt/ems-deploy/app
cp /opt/ems-deploy/app/.env.example /opt/ems-deploy/.env
chmod 600 /opt/ems-deploy/.env
# doplnit secrets / DB / JWT / …
install -m 755 /opt/ems-deploy/app/deploy/deploy.sh /opt/ems-deploy/deploy.sh
/opt/ems-deploy/deploy.sh

Uživatel, pod kterým běží runnerův job (root v Alpine job image), musí mít právo číst .env, psát do app/.git a volat Docker. Typicky runner běží jako root → ověř oprávnění na /opt/ems-deploy.


5. Gitea runner — nutná úprava config.yaml

Aby job směl přimountovat hostové cesty, v runner/config.yaml (na serveru v /opt/gitea-stack/runner/config.yaml) musí být v container.valid_volumes povolené alespoň:

container:
  network: host
  privileged: false
  valid_volumes:
    - /opt/ems-deploy
    - /var/run/docker.sock

Bez toho Actions mounty odmítnou / job spadne. Po změně restart runner kontejneru.

labels při registraci runneru musí odpovídat runs-on ve workflow (např. výchozí self-hosted).


6. Caddy a EMS (až bude veřejná doména)

Gitea blok v Caddyfile zůstává. Pro EMS přidej samostatný site blok, např.:

ems.vojacek.eu {
  encode gzip
  @api path /rest*
  handle @api {
    uri strip_prefix /rest
    reverse_proxy 127.0.0.1:8080
  }
  handle {
    reverse_proxy 127.0.0.1:8080
  }
}

Upřesnění podle skutečného nginx ve frontend image: pokud API jde přes stejný origin a path /rest, výše může stačit jeden reverse_proxy na 127.0.0.1:8080 bez stripu — ověř v frontend konfiguraci. Pro PostgREST OpenAPI nastav v .env / PGRST_OPENAPI_SERVER_PROXY_URI veřejnou bázi URL.

Produkční compose mapuje frontend na 127.0.0.1:8080, backend na 127.0.0.1:8000.


7. CI: test vs deploy

Workflow Účel Poznámka
.gitea/workflows/test.yml Smoke (soubory, layout) runs-on: ubuntu-latest stáhne image; nepotřebuje Docker.
.gitea/workflows/deploy.yml Deploy po pushi na main Job kontejner + mounty + viz sekce 5.

workflow_dispatch na deploy umožňuje ruční opakování bez prázdného commitu.


8. Co záměrně neděláme (zatím)

  • Privátní Docker registry na stejném stroji.
  • Buildkit cache export / remote cache.
  • k3s/Kubernetes.
  • Spouštění docker compose uvnitř jobu bez přístupu k jednomu hostovskému daemonu (tj. bez soku bys musel do DinD nebo remote docker).

9. Rychlá kontrola po deployi

docker compose -f /opt/ems-deploy/docker-compose.yml --env-file /opt/ems-deploy/.env ps
curl -sf http://127.0.0.1:8000/docs >/dev/null && echo backend OK
curl -sf http://127.0.0.1:8080/ >/dev/null && echo frontend OK

10. Odkazy v repu

  • deploy/deploy.sh — jediný produkční vstup „co se na serveru spouští“.
  • deploy/docker-compose.yml — šablona runtime Compose (kopíruje se do /opt/ems-deploy).
  • .gitea/workflows/deploy.yml — napojení na runner + job kontejner.