Files
ROLAC/deploy/nas
Chris Chen ef3731ba48
ci-cd-nas / build-push (push) Failing after 11s
ci-cd-nas / deploy (push) Has been skipped
Update runnder
2026-06-20 22:36:07 -07:00
..
wip
2026-06-20 15:13:23 -07:00
wip
2026-06-20 15:13:23 -07:00
2026-06-20 22:36:07 -07:00

Deploy to Synology NAS (Container Manager) — LAN / HTTP

Target: run the ROLAC stack on a Synology DS220+ (Celeron J4025 / 2GB RAM), reachable on the LAN at http://<nas-ip>:8080. Images are built on the dev PC (the NAS is too weak to compile — Angular's build alone can need >2GB RAM), pushed to the Gitea registry on the NAS, and the NAS only pulls + restarts the containers.

                 push main
   dev PC  ───────────────►  Gitea (NAS)
   (runner: builder)            │ triggers .gitea/workflows/ci-cd-nas.yml
   test + build + push ─────────┤
                                ▼
                          NAS runner (label: nas)  ── deploy only ──┐
                                                                    ▼
browser (LAN)  ->  http://<nas-ip>:8080
                        │  nginx edge (container, 8080->80)
                        ├── /        -> app  container (Angular static)
                        └── /api/    -> api  container (ASP.NET, :8080)
api  ──> existing PostgreSQL @ 192.168.68.55:49154   (not containerized)

Why this split: DS220+ can comfortably run these lightweight containers (nginx + precompiled .NET + static files) but cannot build them. So building (test, dotnet publish, ng build) runs on the dev PC; the NAS just pulls.

Differences vs the Azure plan: no TLS/certbot, edge on 8080 (DSM owns 80/443), reuse the LAN database, deploy via the on-NAS runner (no SSH).


Two runners, two jobs

Job runs-on Where Does
build-push windows dev PC test → build both images → push to registry
deploy nas NAS pull images → docker compose up -d → health check

The dev-PC runner is registered with the label windows:hostruns-on matches the label NAME (windows); :host is the run mode (executes directly on the PC, not in a container, so it uses Docker Desktop + the installed .NET SDK).

deploy has needs: build-push, so it only runs after the build succeeds.


One-time setup — DEV PC (the windows runner) already done

The dev PC runs act_runner natively with the label windows:host, using its installed Docker Desktop + .NET 8 SDK. The workflow's build-push job targets runs-on: windows. Requirements (for reference):

  • Docker Desktop running, and docker on PATH.
  • .NET 8 SDK on PATH (dotnet test runs on this machine).
  • The build-push job uses shell: powershell. Do not switch it to shell: bash: act_runner in Windows host mode mislocates the generated .sh script (/bin/bash: .../1.sh: No such file or directory). PowerShell avoids it.

One-time setup — NAS (the nas runner)

  1. Deploy dir + secrets (via SSH or File Station):

    mkdir -p /volume1/docker/rolac/nginx/conf.d /volume1/docker/rolac/data/api-storage
    cp /path/to/repo/deploy/nas/.env.example /volume1/docker/rolac/.env
    # edit /volume1/docker/rolac/.env  -> real DB user/password + JWT_SECRET + APP_ORIGIN
    
  2. Registry token — in Gitea: Settings → Applications → new token with read:package + write:package. Log the NAS Docker in once:

    docker login git.golife.love -u ChrisChen   # paste the token
    
  3. Install act_runner on the NAS (Container Manager → Registry → gitea/act_runner, or docker run). It must:

    • mount the host Docker socket: -v /var/run/docker.sock:/var/run/docker.sock
    • mount the deploy dir at the same path: -v /volume1/docker/rolac:/volume1/docker/rolac
    • register with the label nas (this is what runs-on: nas targets).
    docker run -d --restart unless-stopped --name rolac-runner \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v /volume1/docker/rolac:/volume1/docker/rolac \
      -e GITEA_INSTANCE_URL=https://git.golife.love \
      -e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
      -e GITEA_RUNNER_LABELS=nas \
      gitea/act_runner:latest
    

One-time setup — Gitea repo

  1. Secrets (Settings → Actions → Secrets):
    • REGISTRY_USER = ChrisChen
    • REGISTRY_TOKEN = the package token (with write:package)
  2. Enable Actions for the repo if not already (Settings → Advanced → Actions).

Day-to-day

git push to main.gitea/workflows/ci-cd-nas.yml:

  1. dev PC (builder): dotnet test → build rolac-api + rolac-app (tags :latest and :<git-sha>) → push to git.golife.love/chrischen/*.
  2. NAS (nas): sync compose/nginx → TAG=<git-sha> docker compose pulldocker compose up -dcurl /api/health.

Open http://<nas-ip>:8080 and log in.

Deploy pins TAG=<git-sha> (not latest), so the NAS always runs exactly the image this commit produced and compose pull forces a fresh fetch.


Manual fallback (no runners yet)

From the dev PC (Docker Desktop + docker login git.golife.love):

# repo root — build + push both images (tags :latest and :<git-sha>)
.\deploy\build-push.ps1

Then on the NAS:

cd /volume1/docker/rolac
docker compose pull
docker compose up -d
curl -fsS http://localhost:8080/api/health

Notes

  • First boot runs DB migrations against 192.168.68.55 automatically (Program.cs calls MigrateAsync() + seed). Make sure the DB user has DDL rights; back up before the first run.
  • Bind-mount paths: the NAS runner runs compose at /volume1/docker/rolac on the host (socket-mounted), so ./nginx/conf.d and ./data resolve to real NAS paths — that's why the runner mounts that dir at the same path.
  • Uploaded files persist under /volume1/docker/rolac/data/api-storage.
  • DS220+ runs, never builds. Keep all compilation on the dev PC / a beefier runner.
  • To expose beyond the LAN later, put it behind DSM's reverse proxy (Application Portal) or switch to the Azure deploy/ files with certbot.