Names the deployed instance slpsoftware, not the software itself

SlpModularCms is the product; slpsoftware.nl is the customer running
it - the first one, and a fine stand-in for what a real deployment
looks like. Renamed the directories, systemd units, and backup paths
accordingly. Left the backup script's own filename alone since the
already-committed deploy-scp.yaml calls it by that exact name.
This commit is contained in:
2026-07-28 22:16:04 +02:00
parent 843253888e
commit 97f18d00e5
3 changed files with 47 additions and 30 deletions
@@ -1323,3 +1323,14 @@ by Infrastructure Design. U5 carries REF-U3-01's Umami origin gate plus FR-21 an
**Context**: OPERATIONS — Deployment Setup, directory naming convention change before final approval **Context**: OPERATIONS — Deployment Setup, directory naming convention change before final approval
--- ---
## Deployment Setup — Instance-Specific Naming (slpsoftware, not slpmodularcms)
**Timestamp**: 2026-07-28T04:20:00Z
**User Input**: "omdat SLP Modular CMS voor meerdere klanten gebruikt kan worden is de mapnaam slpmodularcms niet logisch. kan je in de documentatie of een placeholder gebruiken of slpsoftware. slpsoftware is namenlijk mijn eigen site en de eerste gebruiker dus een prima voorbeeld om te gebruiken"
**AI Response**: "Correct distinction: SlpModularCms is the software (multi-tenant capable), slpsoftware.nl is this specific customer instance. Renamed all instance-scoped naming (directories under ~/apps/ and /mnt/storage1/www/html/, systemd unit names, backup directory, backup credentials file, DB_NAME examples) from slpmodularcms to slpsoftware throughout deployment-instructions.md and rollback-plan.md. Left unchanged what's genuinely software-level, not instance-level: the backup script's own filename (backup-slpmodularcms-db.sh, matching the actual hardcoded reference in the already-committed deploy-scp.yaml), the SlpModularCms.Api.dll binary name, the systemd Description= string, and JwtSettings Issuer/Audience (existing app-config convention from README.md, unrelated to this feature). Added a naming note at the top of deployment-instructions.md explaining the instance-vs-software distinction for future customers."
**Context**: OPERATIONS — Deployment Setup, instance-naming correction before final approval
---
@@ -4,6 +4,12 @@
directory, `systemd --user` unit, and local port. Domains: `test.slpsoftware.nl` (test), directory, `systemd --user` unit, and local port. Domains: `test.slpsoftware.nl` (test),
`slpsoftware.nl` (production). `slpsoftware.nl` (production).
**Naming note**: directories, systemd units and the website-upload path below are named
`slpsoftware` — the **customer/instance** running SlpModularCms (this repo's software), not the
software itself. SlpModularCms can host multiple customers; `slpsoftware` is this one — the first,
and the owner's own site. A future second customer on the same Pi would get its own instance name
throughout, following the same pattern.
--- ---
## 1. One-Time Host Setup ## 1. One-Time Host Setup
@@ -35,7 +41,7 @@ sudo useradd -m -s /bin/bash gitea-workflow
sudo passwd gitea-workflow sudo passwd gitea-workflow
``` ```
Each project deploying through this account gets its own subdirectory under its home (§ 1.4 already Each project deploying through this account gets its own subdirectory under its home (§ 1.4 already
namespaces by project: `~/apps/slpmodularcms/<env>/`), so one generic account can serve multiple namespaces by project: `~/apps/slpsoftware/<env>/`), so one generic account can serve multiple
projects without their release trees colliding. projects without their release trees colliding.
**Fallback, only if no SSH-capable account exists at all and creating one genuinely isn't feasible** **Fallback, only if no SSH-capable account exists at all and creating one genuinely isn't feasible**
@@ -66,25 +72,25 @@ for the CMS's own release/current/shared structure to live anywhere near the oth
which directly avoids interfering with them (as you asked in Q2): which directly avoids interfering with them (as you asked in Q2):
```bash ```bash
mkdir -p ~/apps/slpmodularcms/<env>/releases mkdir -p ~/apps/slpsoftware/<env>/releases
``` ```
`current` is created by the first deploy itself (`ln -sfn`) — don't pre-create it. `current` is created by the first deploy itself (`ln -sfn`) — don't pre-create it.
**`shared/wwwroot-web` is the one exception** — it must resolve to wherever `webadmin` actually **`shared/wwwroot-web` is the one exception** — it must resolve to wherever `webadmin` actually
uploads *this* customer's website via FileZilla, e.g. `/mnt/storage1/www/html/slpmodularcms/<env>/` uploads *this* customer's website via FileZilla, e.g. `/mnt/storage1/www/html/slpsoftware/<env>/`
(adjust the exact folder name to whatever convention the other sites under `html/` already use, if (adjust the exact folder name to whatever convention the other sites under `html/` already use, if
one exists). Rather than a plain directory, make it a symlink across accounts: one exists). Rather than a plain directory, make it a symlink across accounts:
```bash ```bash
mkdir -p ~/apps/slpmodularcms/<env> mkdir -p ~/apps/slpsoftware/<env>
ln -s /mnt/storage1/www/html/slpmodularcms/<env> ~/apps/slpmodularcms/<env>/shared/wwwroot-web ln -s /mnt/storage1/www/html/slpsoftware/<env> ~/apps/slpsoftware/<env>/shared/wwwroot-web
``` ```
`deploy-scp.yaml`'s existing logic (`releases/{ts}/wwwroot/web -> ../../../shared/wwwroot-web`) `deploy-scp.yaml`'s existing logic (`releases/{ts}/wwwroot/web -> ../../../shared/wwwroot-web`)
needs no changes for this — it only ever resolves the symlink chain, it doesn't care how many hops needs no changes for this — it only ever resolves the symlink chain, it doesn't care how many hops
that chain has. What **does** need attention: `gitea-workflow` needs read + traverse permission on that chain has. What **does** need attention: `gitea-workflow` needs read + traverse permission on
`/mnt/storage1/www/html/slpmodularcms/<env>/` and its parent directories, which `webadmin` owns. `/mnt/storage1/www/html/slpsoftware/<env>/` and its parent directories, which `webadmin` owns.
Simplest fix: put both accounts in a shared group (e.g. `webshared`), `chgrp -R webshared` that Simplest fix: put both accounts in a shared group (e.g. `webshared`), `chgrp -R webshared` that
folder, and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just folder, and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just
owner-readable) — a one-time permission setup, not something either pipeline touches per deploy. owner-readable) — a one-time permission setup, not something either pipeline touches per deploy.
@@ -92,15 +98,15 @@ owner-readable) — a one-time permission setup, not something either pipeline t
### 1.5 Runtime Configuration File ### 1.5 Runtime Configuration File
One file per environment, **outside** the release directory so it survives every switch: One file per environment, **outside** the release directory so it survives every switch:
```bash ```bash
touch ~/apps/slpmodularcms/<env>/shared/env touch ~/apps/slpsoftware/<env>/shared/env
chmod 600 ~/apps/slpmodularcms/<env>/shared/env chmod 600 ~/apps/slpsoftware/<env>/shared/env
``` ```
Contents (fill in real values — this file is never read by the workflow, only by the systemd unit Contents (fill in real values — this file is never read by the workflow, only by the systemd unit
below): below):
```ini ```ini
ASPNETCORE_ENVIRONMENT=Production ASPNETCORE_ENVIRONMENT=Production
ASPNETCORE_URLS=http://localhost:<port> ASPNETCORE_URLS=http://localhost:<port>
ConnectionStrings__DefaultConnection=Server=127.0.0.1,1433;User ID=<user>;Password=<password>;Database=SlpModularCms<Env>;TrustServerCertificate=True ConnectionStrings__DefaultConnection=Server=127.0.0.1,1433;User ID=<user>;Password=<password>;Database=SlpSoftware<Env>;TrustServerCertificate=True
JwtSettings__Secret=<secure-long-random-secret> JwtSettings__Secret=<secure-long-random-secret>
JwtSettings__Issuer=SlpModularCms JwtSettings__Issuer=SlpModularCms
JwtSettings__Audience=SlpModularCmsPortal JwtSettings__Audience=SlpModularCmsPortal
@@ -120,16 +126,16 @@ gate only catches drift between the frontend build and that Gitea variable; it c
so keeping the two in sync is a manual discipline, not something enforced automatically. so keeping the two in sync is a manual discipline, not something enforced automatically.
### 1.6 systemd User Units ### 1.6 systemd User Units
Create `~/.config/systemd/user/slpmodularcms-test.service`: Create `~/.config/systemd/user/slpsoftware-test.service`:
```ini ```ini
[Unit] [Unit]
Description=SlpModularCms API (test) Description=SlpModularCms API (test)
After=network.target After=network.target
[Service] [Service]
WorkingDirectory=%h/apps/slpmodularcms/test/current WorkingDirectory=%h/apps/slpsoftware/test/current
ExecStart=/usr/bin/dotnet %h/apps/slpmodularcms/test/current/SlpModularCms.Api.dll ExecStart=/usr/bin/dotnet %h/apps/slpsoftware/test/current/SlpModularCms.Api.dll
EnvironmentFile=%h/apps/slpmodularcms/test/shared/env EnvironmentFile=%h/apps/slpsoftware/test/shared/env
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
KillSignal=SIGINT KillSignal=SIGINT
@@ -138,14 +144,14 @@ TimeoutStopSec=20
[Install] [Install]
WantedBy=default.target WantedBy=default.target
``` ```
And `~/.config/systemd/user/slpmodularcms-production.service` — identical, with `test` replaced by And `~/.config/systemd/user/slpsoftware-production.service` — identical, with `test` replaced by
`production` throughout (including the port inside `shared/env`). `production` throughout (including the port inside `shared/env`).
Enable both (does not start them yet — nothing is deployed there until the first CI run): Enable both (does not start them yet — nothing is deployed there until the first CI run):
```bash ```bash
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user enable slpmodularcms-test.service systemctl --user enable slpsoftware-test.service
systemctl --user enable slpmodularcms-production.service systemctl --user enable slpsoftware-production.service
``` ```
### 1.7 nginx Routing ### 1.7 nginx Routing
@@ -177,12 +183,12 @@ feature — assumed already handled the same way the reference project's domains
### 1.8 Database Backup Credentials (§ 4 depends on this) ### 1.8 Database Backup Credentials (§ 4 depends on this)
```bash ```bash
touch ~/.config/slpmodularcms-db-backup.env touch ~/.config/slpsoftware-db-backup.env
chmod 600 ~/.config/slpmodularcms-db-backup.env chmod 600 ~/.config/slpsoftware-db-backup.env
``` ```
```ini ```ini
DB_SERVER=127.0.0.1,1433 DB_SERVER=127.0.0.1,1433
DB_NAME=SlpModularCmsProduction DB_NAME=SlpSoftwareProduction
DB_USER=<a-login-with-backup-database-permission> DB_USER=<a-login-with-backup-database-permission>
DB_PASSWORD=<password> DB_PASSWORD=<password>
``` ```
@@ -202,10 +208,10 @@ change:
| `PI_MAIN_PORT` | secret | SSH port | | `PI_MAIN_PORT` | secret | SSH port |
| `PI_MAIN_USERNAME` | secret | `gitea-workflow` — the generic, host-wide deploy account (§ 1.2), **not** `webadmin`; reuse the reference project's if it already has one | | `PI_MAIN_USERNAME` | secret | `gitea-workflow` — the generic, host-wide deploy account (§ 1.2), **not** `webadmin`; reuse the reference project's if it already has one |
| `PI_MAIN_PASSWORD` | secret | `gitea-workflow`'s password | | `PI_MAIN_PASSWORD` | secret | `gitea-workflow`'s password |
| `DEPLOY_PATH_TEST` | variable | `/home/gitea-workflow/apps/slpmodularcms/test` | | `DEPLOY_PATH_TEST` | variable | `/home/gitea-workflow/apps/slpsoftware/test` |
| `DEPLOY_PATH_PRODUCTION` | variable | `/home/gitea-workflow/apps/slpmodularcms/production` | | `DEPLOY_PATH_PRODUCTION` | variable | `/home/gitea-workflow/apps/slpsoftware/production` |
| `SERVICE_NAME_TEST` | variable | `slpmodularcms-test.service` | | `SERVICE_NAME_TEST` | variable | `slpsoftware-test.service` |
| `SERVICE_NAME_PRODUCTION` | variable | `slpmodularcms-production.service` | | `SERVICE_NAME_PRODUCTION` | variable | `slpsoftware-production.service` |
| `HEALTH_CHECK_URL_TEST` | variable | `https://test.slpsoftware.nl/health` | | `HEALTH_CHECK_URL_TEST` | variable | `https://test.slpsoftware.nl/health` |
| `HEALTH_CHECK_URL_PRODUCTION` | variable | `https://slpsoftware.nl/health` | | `HEALTH_CHECK_URL_PRODUCTION` | variable | `https://slpsoftware.nl/health` |
| `VITE_SENTRY_DSN` | variable | Sentry DSN (shared, not sensitive — safe in the client bundle) | | `VITE_SENTRY_DSN` | variable | Sentry DSN (shared, not sensitive — safe in the client bundle) |
@@ -223,7 +229,7 @@ Already built (U5/U6) — this is the read-only walkthrough for whoever operates
2. `publish-test` runs (and `publish-production`, only if `workflow_dispatch` with the flag) 2. `publish-test` runs (and `publish-production`, only if `workflow_dispatch` with the flag)
3. `deploy-test` (always, if gates pass) calls `deploy-scp.yaml`, which uploads into a new 3. `deploy-test` (always, if gates pass) calls `deploy-scp.yaml`, which uploads into a new
`releases/{timestamp}/`, links `shared/wwwroot-web` in, switches `current`, restarts `releases/{timestamp}/`, links `shared/wwwroot-web` in, switches `current`, restarts
`slpmodularcms-test.service`, verifies `https://test.slpsoftware.nl/health`, then prunes old `slpsoftware-test.service`, verifies `https://test.slpsoftware.nl/health`, then prunes old
releases (only test — `run_db_backup: false`) releases (only test — `run_db_backup: false`)
4. `deploy-production` (only with the flag) does the same, plus a database backup first 4. `deploy-production` (only with the flag) does the same, plus a database backup first
(`run_db_backup: true`) — see § 4 (`run_db_backup: true`) — see § 4
@@ -246,7 +252,7 @@ part of this repository, so no DB credential ever reaches Gitea):
set -euo pipefail set -euo pipefail
ENVIRONMENT="${1:?Usage: backup-slpmodularcms-db.sh <environment>}" ENVIRONMENT="${1:?Usage: backup-slpmodularcms-db.sh <environment>}"
CREDENTIALS_FILE="$HOME/.config/slpmodularcms-db-backup.env" CREDENTIALS_FILE="$HOME/.config/slpsoftware-db-backup.env"
if [[ ! -f "$CREDENTIALS_FILE" ]]; then if [[ ! -f "$CREDENTIALS_FILE" ]]; then
echo "Missing $CREDENTIALS_FILE — see deployment-instructions.md § 1.8" >&2 echo "Missing $CREDENTIALS_FILE — see deployment-instructions.md § 1.8" >&2
@@ -256,7 +262,7 @@ fi
source "$CREDENTIALS_FILE" source "$CREDENTIALS_FILE"
: "${DB_SERVER:?}" "${DB_NAME:?}" "${DB_USER:?}" "${DB_PASSWORD:?}" : "${DB_SERVER:?}" "${DB_NAME:?}" "${DB_USER:?}" "${DB_PASSWORD:?}"
BACKUP_DIR="$HOME/backups/slpmodularcms/${ENVIRONMENT}" BACKUP_DIR="$HOME/backups/slpsoftware/${ENVIRONMENT}"
mkdir -p "$BACKUP_DIR" mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date -u +%Y%m%d%H%M%S) TIMESTAMP=$(date -u +%Y%m%d%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.bak" BACKUP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.bak"
@@ -278,7 +284,7 @@ chmod +x ~/scripts/backup-slpmodularcms-db.sh
```bash ```bash
~/scripts/backup-slpmodularcms-db.sh production ~/scripts/backup-slpmodularcms-db.sh production
``` ```
Confirm a `.bak` file appears under `~/backups/slpmodularcms/production/` and that `sqlcmd` didn't Confirm a `.bak` file appears under `~/backups/slpsoftware/production/` and that `sqlcmd` didn't
silently fail (the script uses `set -euo pipefail`, so a real SQL error does propagate as a non-zero silently fail (the script uses `set -euo pipefail`, so a real SQL error does propagate as a non-zero
exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy). exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy).
@@ -16,10 +16,10 @@ failed deploy never leaves fewer than two releases on disk.
No rebuild needed. Over SSH, on the Pi: No rebuild needed. Over SSH, on the Pi:
```bash ```bash
cd ~/apps/slpmodularcms/<env>/releases cd ~/apps/slpsoftware/<env>/releases
ls -1t # confirm which directory is the previous, working release ls -1t # confirm which directory is the previous, working release
ln -sfn ~/apps/slpmodularcms/<env>/releases/<previous-timestamp> ~/apps/slpmodularcms/<env>/current ln -sfn ~/apps/slpsoftware/<env>/releases/<previous-timestamp> ~/apps/slpsoftware/<env>/current
systemctl --user restart slpmodularcms-<env>.service systemctl --user restart slpsoftware-<env>.service
curl -f https://<env-domain>/health # confirm the rollback itself is healthy curl -f https://<env-domain>/health # confirm the rollback itself is healthy
``` ```