2026-05-02 08:26:18 +12:00
# Deployment
2026-05-02 12:39:55 +12:00
## Server layout confirmed
2026-05-02 08:26:18 +12:00
2026-05-02 12:39:55 +12:00
The production server currently runs multiple separate Docker Compose projects:
2026-05-02 08:26:18 +12:00
2026-05-02 12:39:55 +12:00
- Main public site WordPress stack:
- project: `goodwalkconz`
- path: `/docker/wordpress/goodwalk.co.nz`
- Onboarding WordPress stack:
- project: `onboardinggoodwalkconz`
- path: `/docker/wordpress/onboarding.goodwalk.co.nz`
- Shared nginx:
- project: `nginx`
- path: `/docker/nginx`
- Shared mysql:
- project: `mysql`
- path: `/docker/mysql`
2026-05-02 08:26:18 +12:00
2026-05-02 12:39:55 +12:00
The deployment scripts in this repo are set up to deploy the new Svelte site as a
separate stack at:
2026-05-02 08:26:18 +12:00
2026-05-02 12:39:55 +12:00
- remote path: `/docker/goodwalk-svelte`
- compose file: `docker-compose.prod.yml`
- docker project: `goodwalk-svelte`
2026-05-02 08:26:18 +12:00
2026-05-02 12:39:55 +12:00
This leaves the onboarding site, shared nginx, shared mysql, and other unrelated
containers untouched.
## Files involved
- [deploy.ps1 ](deploy.ps1 )
- Windows entrypoint for packaging the repo, uploading it, and running the
remote deployment helper over SSH.
2026-05-03 15:56:04 +12:00
- [scripts/deploy.ps1 ](scripts/deploy.ps1 )
- Deprecated compatibility wrapper that forwards to the repo-root
`deploy.ps1` . Keep using the root script directly.
2026-05-02 12:39:55 +12:00
- [scripts/deploy-remote.sh ](scripts/deploy-remote.sh )
- Server-side helper that updates only the `goodwalk-svelte` compose project.
- [docker-compose.prod.yml ](docker-compose.prod.yml )
- Production compose file for the new Svelte app, mail API, and Postgres.
2026-05-02 19:44:45 +12:00
- `scripts/export-homepage-content.mjs`
- Local helper that exports the current `src/lib/content/homepage.ts` into a
deployable JSON payload before each deployment.
- `scripts/sync-homepage-content.mjs`
- Runtime helper that upserts the exported homepage content into PostgreSQL
after deploys that affect the app/database.
2026-05-02 12:39:55 +12:00
- [ssh-config ](ssh-config )
- Repo-local SSH config used by the deployment script.
- [nginx/goodwalk.co.nz.svelte.conf.example ](nginx/goodwalk.co.nz.svelte.conf.example )
- Example shared-nginx config for routing the main public site to the new
Svelte app and mail API.
## First-time server preparation
1. Fill in [ssh-config ](ssh-config ) with the real host details.
2. Create the deployment directory on the server:
``` bash
mkdir -p /docker/goodwalk-svelte
```
3. The first deployment will auto-create the production env file on the server at:
``` bash
/docker/goodwalk-svelte/.env
```
It is created from [deploy.env.template ](deploy.env.template ). Current template contents:
``` env
2026-05-02 19:44:45 +12:00
APP_VERSION = 4.0.1
TZ = Pacific/Auckland
2026-05-02 12:39:55 +12:00
POSTGRES_DB = goodwalk
POSTGRES_USER = goodwalk
POSTGRES_PASSWORD = gw_Pg_7Jm9!Qx4#Ld2@Vr8
2026-05-02 19:44:45 +12:00
POSTGRES_PASSWORD_URLENCODED = gw_Pg_7Jm9%21Qx4%23Ld2%40Vr8
2026-05-02 12:39:55 +12:00
RESEND_API_KEY = replace-me
OWNER_EMAIL = replace-me
FROM_EMAIL = GoodWalk <bookings@goodwalk.co.nz>
REPLY_TO = aless@goodwalk.co.nz
FORM_MIN_SECONDS = 4
FORM_MAX_SECONDS = 7200
RATE_LIMIT_WINDOW_SECONDS = 900
RATE_LIMIT_MAX_PER_IP = 5
RATE_LIMIT_MAX_PER_EMAIL = 3
RATE_LIMIT_MIN_INTERVAL_SECONDS = 20
```
After the first deploy, edit `/docker/goodwalk-svelte/.env` on the server and replace:
- `RESEND_API_KEY=replace-me`
- `OWNER_EMAIL=replace-me`
4. Confirm the shared Docker network already exists:
``` bash
docker network ls | grep webnet
```
Your server already uses `webnet` , so this should already be present.
## First deploy
From Windows PowerShell in the repo root:
``` powershell
powershell -ExecutionPolicy Bypass -File . \ deploy . ps1
```
2026-05-03 15:56:04 +12:00
This is the single supported deployment entrypoint. If you see
`scripts/deploy.ps1` , that file now just forwards to the root script so the
deployment logic only lives in one place.
2026-05-02 12:39:55 +12:00
Or skip the confirmation prompt:
2026-05-02 08:26:18 +12:00
``` powershell
2026-05-02 12:39:55 +12:00
powershell -ExecutionPolicy Bypass -File . \ deploy . ps1 -Force
```
2026-05-02 19:44:45 +12:00
To rebuild and restart only one service, for example the mail API:
``` powershell
powershell -ExecutionPolicy Bypass -File . \ deploy . ps1 -Force -Service mail-api
```
## Homepage content sync
Local development can feel fresher than production because production reads the
homepage/shared content from PostgreSQL whenever `DATABASE_URL` is set.
The deployment flow now handles that automatically:
1. `deploy.ps1` exports the current `src/lib/content/homepage.ts` into
`deploy-data/homepage-content.json` .
2. The deploy archive uploads that JSON payload with the app source.
3. After the Goodwalk stack is updated, the remote helper runs a content sync
inside the app container.
4. That sync upserts the `homepage` row in `site_content` .
This means future deploys will carry your latest file-based homepage/navigation/
shared content changes into production PostgreSQL automatically.
2026-05-02 12:39:55 +12:00
## Cutover nginx
After the new Svelte stack is up and healthy, update the shared nginx config on
the server for the main site.
Current live file:
``` bash
/docker/nginx/conf.d/goodwalk.co.nz.conf
```
Use the repo example as the new target config:
``` bash
nginx/goodwalk.co.nz.svelte.conf.example
```
2026-05-02 19:44:45 +12:00
Important:
2026-05-03 11:16:53 +12:00
- `deploy.ps1` now copies the repo nginx config to `/docker/nginx/conf.d/goodwalk.co.nz.conf` and reloads the shared nginx container as part of deployment.
- The repo nginx config uses Docker's internal resolver so future app/mail container rebuilds will not leave nginx pinned to stale upstream IPs.
2026-05-02 19:44:45 +12:00
2026-05-03 11:16:53 +12:00
Manual nginx commands, if you ever need them:
2026-05-02 12:39:55 +12:00
``` bash
docker compose -p nginx -f /docker/nginx/docker-compose.yml exec nginx nginx -t
docker compose -p nginx -f /docker/nginx/docker-compose.yml exec nginx nginx -s reload
```
## Important notes
- Do not deploy the top-level `docker-compose.yml` to this server for production.
It includes its own nginx service and does not match the shared nginx setup on
the host.
- The deployment scripts do not stop or remove the onboarding WordPress stack.
- The deployment scripts do not touch the shared mysql compose project.
- The deployment scripts preserve the remote `.env` file.
- The site check in `deploy.ps1` targets `https://www.goodwalk.co.nz/api/health` .
Before nginx cutover, use `-SkipSiteCheck` or expect that check to fail.