- Central nginx is containerized and proxies via host IP (171.22.25.73:port), not localhost → publish app on host :2569 (was 127.0.0.1)
- nginx vhost rewritten to match the monolithic config style (server blocks to paste into http{}, manual /etc/ssl/hamkadr certs, proxy_pass 171.22.25.73:2569, $connection_upgrade)
- DEPLOY.md: corrected architecture/ports, removed certbot+sites-available (use manual certs + single nginx.conf)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.3 KiB
Deploying همکادر / hamkadr.ir
CI/CD via the soroush method: push to Gitea → Gitea Actions builds (through the Nexus mirror)
and the self-hosted runner deploys with Docker Compose. nginx (already on the server) terminates
TLS for hamkadr.ir and reverse-proxies to the app.
Architecture & open ports
Internet ─443/80─► central nginx (container) ─► http://171.22.25.73:2569 ─► hamkadr_api (container :8080)
(nexus/mirror/gitea/meezi…) host-published port │ internal docker net
▼
hamkadr_db (postgres, no host port)
Matches the existing soroush pattern: the containerized central nginx reaches each app via the
host IP + published port (171.22.25.73:<port>), not localhost. So hamkadr publishes 2569 on
the host (like meezi 5080, draletaha 5010…).
| Port | Open? | Purpose |
|---|---|---|
| 22 | ✅ (ideally IP-restricted) | SSH |
| 80 / 443 | ✅ (already open) | central nginx — serves hamkadr.ir too |
| 2569 | host-published | app; only nginx proxies to it. Optionally firewall to the nginx host. |
| 5432 | ❌ internal docker net only | Postgres — never published |
No firewall change needed for 80/443 (nginx already serves git./mirror./meezi). 2569 is published on the host like the other apps.
Files in this repo
| File | Role |
|---|---|
Dockerfile |
multi-stage build, images + NuGet via mirror.soroushasadi.com |
nuget.docker.config |
NuGet → Nexus nuget-group |
docker-compose.yml |
production stack: api (host :${HOST_PORT}) + db (internal) + named volume |
docker-compose.dev.yml |
local-dev Postgres only (host 5433) for dotnet run |
.gitea/workflows/ci-cd.yml |
build job + self-hosted deploy (backup → rollback tag → recreate → health-wait) |
deploy/nginx-hamkadr.ir.conf |
nginx vhost for hamkadr.ir |
One-time setup
1. DNS
A records → 171.22.25.73:
hamkadr.ir A 171.22.25.73
www.hamkadr.ir A 171.22.25.73
2. Gitea runner
Confirm the act_runner on this server has the self-hosted:host label (the deploy job needs it)
and its user is in the docker group. (Already true if other soroush projects deploy here.)
3. ENV_FILE secret
Set at https://git.soroushasadi.com/soroushdes/hamkadr/settings/secrets → key ENV_FILE:
docker-compose.yml substitutes these into the api/db services (it derives
ConnectionStrings__Default and Auth__AdminPhone for you), so the secret is just:
# host port nginx proxies to (must match deploy/nginx-hamkadr.ir.conf)
HOST_PORT=2569
# Postgres — generate a strong password: openssl rand -hex 24
POSTGRES_DB=hamkadr
POSTGRES_USER=hamkadr
POSTGRES_PASSWORD=__CHANGE_ME__
# Platform admin phone (gets the Admin role on login)
ADMIN_PHONE=09XXXXXXXXX
That's the whole secret. Everything else — the AI audit layer and the channel sources (Telegram channels, Bale bot token, Divar queries, auto-ingest on/off + interval) — is configured at runtime in the admin panel (
/Admin/Settings), stored in the DB. No redeploy to change them. Defaults: AI off, mode = Manual, all sources off ⇒ nothing publishes without admin review.ASPNETCORE_ENVIRONMENT=Productionis set by the compose file ⇒ only reference data (roles/cities/districts) is seeded — no demo facilities/shifts.
4. TLS cert + nginx vhost
Your central nginx is a single monolithic nginx.conf with manually-placed certs (no
certbot). Match that:
- Put the hamkadr.ir cert where nginx expects (same convention as your other domains):
/etc/ssl/hamkadr/fullchain.pem /etc/ssl/hamkadr/privateKey.pem - Paste the two
server { }blocks fromdeploy/nginx-hamkadr.ir.confinto thehttp { }block of your central nginx.conf (next to meezi/draletaha). They proxy tohttp://171.22.25.73:2569and reuse the global$connection_upgrademap. - Reload:
nginx -t && nginx -s reload # or: docker exec <nginx-container> nginx -s reload
5. First deploy
git push gitea main # add the gitea remote first if needed
Watch https://git.soroushasadi.com/soroushdes/hamkadr/actions. The app auto-applies EF migrations
on startup and seeds reference data; nginx already proxies hamkadr.ir to it.
Operations
- Backups: every deploy runs
pg_dump→/opt/hamkadr-backups/hamkadr-<timestamp>.sqlbefore touching containers. - Rollback: the previous image is tagged
mirror.soroushasadi.com/hamkadr/api:rollbackeach deploy:docker stop hamkadr_api && docker rm hamkadr_api API_TAG=rollback docker compose up -d --no-deps api - Rotate a secret: edit
ENV_FILEin Gitea, push any commit to redeploy. - Logs:
docker logs -f hamkadr_api - Restore a backup:
cat /opt/hamkadr-backups/<file>.sql | docker exec -i hamkadr_db psql -U hamkadr -d hamkadr
Safety (never do these)
- ❌
docker compose down -v— deletes the database volume. - ❌ bare
docker compose down/restart— would stop other projects on the shared host. The workflow always uses--no-deps <service>and explicitstop/rm.