Add Gitea CI/CD for hamkadr.ir (Nexus build + self-hosted compose deploy)
- .gitea/workflows/ci-cd.yml: dotnet build via mirror.soroushasadi.com; self-hosted deploy with pg_dump backup, rollback tag, scoped recreate, /healthz wait, prune - Dockerfile (sdk/aspnet 10 via Nexus) + nuget.docker.config + .dockerignore - docker-compose.prod.yml: app on 127.0.0.1:APP_PORT, Postgres internal-only + named volume - deploy/nginx-hamkadr.ir.conf + DEPLOY.md (ports: 22/80/443 only; DB never exposed) - Prod seeds reference data only (no demo listings); ForwardedHeaders for nginx TLS; /healthz endpoint Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# 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──► nginx (host, existing) ──► 127.0.0.1:8090 ──► hamkadr-app (container :8080)
|
||||
│ internal docker net
|
||||
▼
|
||||
hamkadr-db (postgres, no host port)
|
||||
```
|
||||
|
||||
| Port | Open? | Purpose |
|
||||
|------|-------|---------|
|
||||
| 22 | ✅ (ideally IP-restricted) | SSH |
|
||||
| 80 | ✅ | HTTP → 443 redirect + Let's Encrypt ACME |
|
||||
| 443 | ✅ | HTTPS `hamkadr.ir` |
|
||||
| 8090 | ❌ host-localhost only | app, reached only by nginx |
|
||||
| 5432 | ❌ internal docker net only | Postgres — never published |
|
||||
|
||||
`ufw` should be exactly: `allow 22, 80, 443`. Nothing else. (80/443 are already open since nginx
|
||||
serves git./mirror. — no firewall change needed.)
|
||||
|
||||
## 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.prod.yml` | `app` (127.0.0.1:${APP_PORT}) + `db` (internal) + named volume |
|
||||
| `.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 → server IP:
|
||||
```
|
||||
hamkadr.ir A <server-ip>
|
||||
www.hamkadr.ir A <server-ip>
|
||||
```
|
||||
|
||||
### 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`**:
|
||||
|
||||
```dotenv
|
||||
ASPNETCORE_ENVIRONMENT=Production
|
||||
ASPNETCORE_URLS=http://+:8080
|
||||
|
||||
# host port nginx proxies to (must match deploy/nginx-hamkadr.ir.conf)
|
||||
APP_PORT=8090
|
||||
|
||||
# Postgres (container) — generate a strong password: openssl rand -hex 24
|
||||
POSTGRES_DB=hamkadr
|
||||
POSTGRES_USER=hamkadr
|
||||
POSTGRES_PASSWORD=__CHANGE_ME__
|
||||
|
||||
# EF Core connection string (host = compose service name "db")
|
||||
ConnectionStrings__Default=Host=db;Port=5432;Database=hamkadr;Username=hamkadr;Password=__CHANGE_ME__
|
||||
|
||||
# Platform admin (the phone that gets the Admin role on login)
|
||||
Auth__AdminPhone=09XXXXXXXXX
|
||||
|
||||
# Future: Kavenegar / SMS.ir keys for real OTP delivery
|
||||
```
|
||||
> `POSTGRES_PASSWORD` and the password in `ConnectionStrings__Default` must be identical.
|
||||
> `ASPNETCORE_ENVIRONMENT=Production` ⇒ only **reference data** (roles/cities/districts) is seeded —
|
||||
> no demo facilities/shifts. Real employers add listings via the employer panel.
|
||||
|
||||
### 4. nginx vhost + TLS
|
||||
```bash
|
||||
sudo cp deploy/nginx-hamkadr.ir.conf /etc/nginx/sites-available/hamkadr.ir
|
||||
sudo ln -s /etc/nginx/sites-available/hamkadr.ir /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
sudo certbot --nginx -d hamkadr.ir -d www.hamkadr.ir
|
||||
```
|
||||
|
||||
### 5. First deploy
|
||||
```bash
|
||||
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>.sql` before touching containers.
|
||||
- **Rollback:** the previous image is tagged `hamkadr-app:rollback` each deploy:
|
||||
```bash
|
||||
docker stop hamkadr-app && docker rm hamkadr-app
|
||||
docker run -d --name hamkadr-app --env-file .env --network hamkadr_default \
|
||||
-p 127.0.0.1:8090:8080 hamkadr-app:rollback
|
||||
```
|
||||
- **Rotate a secret:** edit `ENV_FILE` in Gitea, push any commit to redeploy.
|
||||
- **Logs:** `docker logs -f hamkadr-app`
|
||||
- **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 explicit `stop`/`rm`.
|
||||
Reference in New Issue
Block a user