# Deploy — CTAO portal demo on strapi-experimental.cyfronet Everything runs rootless as user `strapi`. Footprint on the machine: ONE directory (`~/ctao-portal-demo/`) + 4 unit files in `~/.config/`. Uninstall restores the machine exactly (see bottom). No secrets in any file. ``` ~/ctao-portal-demo/ ├── gitea-data/ # Gitea state (repos, SQLite, accounts) ← the ONLY thing worth backing up ├── gitea-config/ # Gitea app.ini (generated from env) ├── repo/ # clone of the portal repo (created by first build) ├── releases/ # / dirs + `current` symlink (what nginx serves) ├── state/ # last-built SHA ├── bin/build.sh # copied from deploy/ (source of truth stays in the repo) └── config/nginx.conf ``` | Port | What | Exposed how | |---|---|---| | 3000 | Gitea | ingress vhost (TODO: ask Hubert) | | 8080 | portal (nginx, static) | ingress vhost (TODO: ask Hubert) | ## Install (each step reviewed before running; [W] = writes to the machine) 1. **[W]** `mkdir -p ~/ctao-portal-demo/{gitea-data,gitea-config,releases,state,bin,config} ~/.config/containers/systemd ~/.config/systemd/user` 2. **[W]** Copy files from this dir (scp from the Mac): - `ctao-demo-gitea.container`, `ctao-demo-web.container` → `~/.config/containers/systemd/` - `ctao-portal-build.service`, `ctao-portal-build.timer` → `~/.config/systemd/user/` - `build.sh` → `~/ctao-portal-demo/bin/` (`chmod +x`) - `nginx.conf` → `~/ctao-portal-demo/config/` 3. **[W]** Pull + pin images (one-time, needs internet): `podman pull docker.io/gitea/gitea:1.27-rootless docker.io/library/nginx:stable-alpine docker.io/library/node:24-alpine` `podman build -t localhost/ctao-portal-build:1 -f Containerfile.build .` Then `podman images --digests` → paste the sha256 digests into both `.container` files. 4. **[W]** `systemctl --user daemon-reload && systemctl --user start ctao-demo-gitea` 5. **[W]** Create the Gitea admin — run YOURSELF in your own terminal (password is prompted/printed there only; never goes through chat or shell history): `podman exec -it ctao-demo-gitea gitea admin user create --admin --username --email --random-password` 6. **[W]** In the Gitea UI: create org `ctao`, repo `portal` (public read). Push from the Mac through an SSH port-forward: `ssh -L 3300:localhost:3000 strapi-experimental.cyfronet` then `git remote add machine http://localhost:3300/ctao/portal.git && git push machine main` 7. **[W]** `systemctl --user enable --now ctao-portal-build.timer` — first run clones + `npm ci` + builds (minutes); later runs are seconds. Wait until `journalctl --user -u ctao-portal-build -n 5` shows `published ` (starting nginx earlier just serves 404s until the first build lands). 8. **[W]** `systemctl --user start ctao-demo-web` 9. **[R]** Verify: `curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/` and `journalctl --user -u ctao-portal-build -n 20` (shows measured build times). ## After Hubert assigns the vhosts 1. Replace both `TODO(vhost)` values in `ctao-demo-gitea.container` (ROOT_URL → gitea vhost, CORS `*` → portal origin, add `GITEA__cors__SCHEME=https`); `systemctl --user daemon-reload && systemctl --user restart ctao-demo-gitea`. 2. In Gitea UI: Settings → Applications → new OAuth2 app for Sveltia (redirect: `https:///admin/`), PKCE, no client secret. 3. Put the Gitea vhost URL + client id into `public/admin/config.yml` in the portal repo, commit, push — the timer publishes it like any other change. ## Ask Hubert (one message) 1. Two ingress vhosts → `192.168.10.15:8080` (portal) and `:3000` (gitea) — same mechanism as `strapi.isl-dev…:1337`. 2. Are vhosts public-internet or VPN-scopable? (Gitea preferably VPN-only.) 3. Does Cyfronet offer static-file hosting on the ingress itself? If yes, we drop our nginx container entirely and rsync builds there instead. ## Uninstall (leaves zero traces) ``` systemctl --user disable --now ctao-portal-build.timer systemctl --user stop ctao-demo-web ctao-demo-gitea rm ~/.config/containers/systemd/ctao-demo-*.container \ ~/.config/systemd/user/ctao-portal-build.{service,timer} systemctl --user daemon-reload podman rmi localhost/ctao-portal-build:1 docker.io/gitea/gitea:1.27-rootless docker.io/library/nginx:stable-alpine rm -rf ~/ctao-portal-demo ``` ## Notes - Publish latency = poll (≤10 s) + build (measured 1 s on M-series; expect 4–8 s on the 2 vCPU VM — every build's time lands in the journal). - Internet needed only for: image pulls (install) and `npm ci` when the lockfile changes. Routine rebuilds are fully offline. - Memory caps (`MemoryHigh`) keep us polite next to Outline + Strapi; the build container is capped at 1 GB via `podman run --memory`. Watch the first `npm ci` + build in the journal — if it OOMs inside its cgroup (contained, just retries), raise the cap. - Build isolation: build containers join the Gitea container's network namespace (`--network=container:ctao-demo-gitea`) — repo/npm code sees Gitea on localhost:3000 but cannot reach host loopback services. (Rootless netavark bridges don't work here: no `ip_tables` kernel module, no sudo.) - Secrets inventory: Gitea admin password (typed interactively, lives only in Gitea's DB) — that's the complete list. Build/poll/serve use none.