2026-07-04 Mac mini deployment work summary
Overview
Today the Mac mini deployment environment was organized around PM2, Caddy, PostgreSQL, and a token-protected TypeScript deploy API.
Primary goals completed:
- Persisted
qrstay.dongholab.comCaddy routing into/etc/caddy/Caddyfile. - Installed and configured PostgreSQL for deploy-run storage.
- Brought up
deploy-apiunder PM2 on127.0.0.1:3300. - Exposed
deploy-apithrough Caddy ashttps://pm2-deploy.dongholab.com. - Connected
qrstayto the deploy API flow. - Fixed PM2 environment leakage during cross-service reloads.
- Added automatic deployment status documentation into this repository.
Repositories touched
pm2-multi-service-starter
Repository:
Important commits:
5ca2a1f—feat: add TypeScript deploy API starterfd14379—feat: record deploy status to Git repo
Major additions:
- TypeScript deploy API under
services/deploy-api. - PostgreSQL-backed
deploy_runsstorage with JSONL fallback. deploy.config.jsonruntime config for services.- GitHub Actions deploy workflow example.
- Caddy/deploy architecture documentation.
- Status repo writer that creates Markdown and JSON deployment records.
Runtime config:
- Deploy API port:
127.0.0.1:3300 - Public endpoint:
https://pm2-deploy.dongholab.com - Status repo path:
/Users/dongho/macmini-deploy-status
qr-stay
Repository:
Important commit:
3996f1e—chore: add PM2 production ecosystem config
Major additions:
ecosystem.config.cjsfor production PM2 execution.- Canonical qrstay runtime env:
PORT=3010HOSTNAME=127.0.0.1NODE_ENV=production
macmini-deploy-status
Repository:
Important commits:
68b5295— initial repository setup.21670c7— first automatic qrstay deployment status commit.
Purpose:
- Store Mac mini deployment status snapshots by date and service.
Directory convention:
releases/YYYY-MM-DD/<service>/
Each deployment creates:
latest.mdruns/<timestamp>-<run-id>.mdruns/<timestamp>-<run-id>.json
Caddy configuration
Static Caddyfile path:
/etc/caddy/Caddyfile
Persisted routes:
qrstay.dongholab.com {
reverse_proxy 127.0.0.1:3010
}
pm2-deploy.dongholab.com {
reverse_proxy 127.0.0.1:3300
}
Validation performed:
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfilecaddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
Backups created during Caddy changes:
/etc/caddy/Caddyfile.backup-20260704-165931/etc/caddy/Caddyfile.backup-deploy-api-20260704-172249
PostgreSQL setup
Installed service:
postgresql@16
Homebrew service status:
postgresql@16 started
Deploy DB:
- Database:
pm2_deploy - User:
pm2_deploy - Table:
deploy_runs
Runtime secret/env file:
/Users/dongho/.config/pm2-multi-service-starter/deploy-api.env
Contains:
DEPLOY_API_PORTDEPLOY_CONFIG_PATHDEPLOY_TOKENDATABASE_URLDEPLOY_STATUS_REPO_PATH
Secret values are intentionally not recorded in this repository.
PM2 services
Relevant services after setup:
deploy-api— online, Node deploy API, listens on127.0.0.1:3300.qrstay— online, Next.js app, listens on3010.pm2-webui— online.
PM2 state was saved with:
pm2 save
Deploy API behavior
Endpoint:
POST https://pm2-deploy.dongholab.com/deploy/<service>
Authentication:
Authorization: Bearer <DEPLOY_TOKEN>- or
x-deploy-token: <DEPLOY_TOKEN>
Current configured service:
qrstay
Deployment sequence:
git fetch origin maingit checkout maingit pull --ff-only origin mainnpm cinpm run buildpm2 reload /Users/dongho/qr-stay/ecosystem.config.cjs --only qrstay --update-envcurl -fsSIL https://qrstay.dongholab.com/ --retry 10 --retry-all-errors --retry-delay 1
Issues found and fixed
PM2 environment leakage
Issue:
deploy-apiusesPORT=3300.- A plain
pm2 reload qrstay --update-envcaused qrstay to inherit the deploy API environment. - qrstay temporarily started on port
3300, while Caddy expected port3010, causing502.
Fix:
- Changed qrstay reload command to use qrstay's own ecosystem file:
pm2 reload /Users/dongho/qr-stay/ecosystem.config.cjs --only qrstay --update-env
Result:
- qrstay consistently runs on port
3010.
Transient Caddy 502 immediately after reload
Issue:
- Healthcheck sometimes hit Caddy immediately after PM2 reload, before the app was ready.
- This caused transient
502failures.
Fix:
- Added curl retries to healthcheck:
curl -fsSIL https://qrstay.dongholab.com/ --retry 10 --retry-all-errors --retry-delay 1
Result:
- Final qrstay deployments pass healthcheck reliably.
PostgreSQL table ownership
Issue:
deploy_runstable was initially owned by the local user.- The
pm2_deployDB user could not insert rows.
Fix:
ALTER TABLE deploy_runs OWNER TO pm2_deploy;
GRANT ALL PRIVILEGES ON TABLE deploy_runs TO pm2_deploy;
Result:
- deploy-api can write deployment records to PostgreSQL.
Deployment run history from DB
Runs recorded today:
| Service | Status | Steps | Started | Finished | Notes |
|---|---|---|---|---|---|
| smoke | success | 4 | 2026-07-04 17:25:42 KST | 2026-07-04 17:25:42 KST | Smoke test for status/DB path |
| qrstay | failed | 7 | 2026-07-04 17:26:11 KST | 2026-07-04 17:26:22 KST | qrstay inherited PORT=3300; Caddy expected 3010 |
| qrstay | failed | 7 | 2026-07-04 17:27:24 KST | 2026-07-04 17:27:35 KST | transient 502 before retry logic |
| qrstay | success | 7 | 2026-07-04 17:28:11 KST | 2026-07-04 17:28:23 KST | deploy path working |
| qrstay | success | 7 | 2026-07-04 17:30:12 KST | 2026-07-04 17:30:24 KST | verified after qrstay repo commit |
| qrstay | success | 7 | 2026-07-04 17:35:55 KST | 2026-07-04 17:36:07 KST | verified automatic status repo commit/push |
Final verification
Verified endpoints:
https://pm2-deploy.dongholab.com/health
https://pm2-deploy.dongholab.com/services
https://qrstay.dongholab.com/
Final known results:
deploy-apihealth endpoint returned OK.deploy-apiservice list returnedqrstay.qrstay.dongholab.comreturned HTTP 200.deploy_runshas qrstay success records.- This status repository received an automatic deployment commit.
Next recommended step
For each additional service:
- Add its PM2
ecosystem.config.cjsin the service repo. - Add a service entry to
/Users/dongho/pm2-multi-service-starter/deploy.config.json. - Add a Caddy block pointing the public domain to the service port.
- Trigger deploy through
pm2-deploy.dongholab.com. - Confirm a new folder appears under
releases/YYYY-MM-DD/<service>/in this repository.