8.2 KiB
Deployment
Server layout confirmed
The production server currently runs multiple separate Docker Compose projects:
- Main public site WordPress stack:
- project:
goodwalkconz - path:
/docker/wordpress/goodwalk.co.nz
- project:
- Onboarding WordPress stack:
- project:
onboardinggoodwalkconz - path:
/docker/wordpress/onboarding.goodwalk.co.nz
- project:
- Shared nginx:
- project:
nginx - path:
/docker/nginx
- project:
- Shared mysql:
- project:
mysql - path:
/docker/mysql
- project:
The deployment scripts in this repo are set up to deploy the new Svelte site as a separate stack at:
- remote path:
/docker/goodwalk-svelte - compose file:
docker-compose.prod.yml - docker project:
goodwalk-svelte
This leaves the onboarding site, shared nginx, shared mysql, and other unrelated containers untouched.
Files involved
- deploy.ps1
- Windows entrypoint for packaging the repo, uploading it, and running the remote deployment helper over SSH.
- scripts/deploy.ps1
- Deprecated compatibility wrapper that forwards to the repo-root
deploy.ps1. Keep using the root script directly.
- Deprecated compatibility wrapper that forwards to the repo-root
- scripts/deploy-remote.sh
- Server-side helper that updates only the
goodwalk-sveltecompose project.
- Server-side helper that updates only the
- scripts/deploy-from-git.sh
- Standalone server-side entrypoint that pulls from Git, then runs the same compose/nginx deployment steps on the server.
- docker-compose.prod.yml
- Production compose file for the new Svelte app, mail API, and Postgres.
scripts/export-homepage-content.mjs- Local helper that exports the current
src/lib/content/homepage.tsinto a deployable JSON payload before each deployment.
- Local helper that exports the current
scripts/sync-homepage-content.mjs- Runtime helper that upserts the exported homepage content into PostgreSQL after deploys that affect the app/database.
- ssh-config
- Repo-local SSH config used by the deployment script.
- 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
-
Fill in ssh-config with the real host details.
-
Create the deployment directory on the server:
mkdir -p /docker/goodwalk-svelte
- The first deployment will auto-create the production env file on the server at:
/docker/goodwalk-svelte/.env
It is created from deploy.env.template. Current template contents:
APP_VERSION=4.2.3
ENABLE_GENERAL_ENQUIRIES=false
PUBLIC_ENABLE_MOBILE_CTA_BUTTON=false
TZ=Pacific/Auckland
POSTGRES_DB=goodwalk
POSTGRES_USER=goodwalk
POSTGRES_PASSWORD=gw_Pg_7Jm9!Qx4#Ld2@Vr8
POSTGRES_PASSWORD_URLENCODED=gw_Pg_7Jm9%21Qx4%23Ld2%40Vr8
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-meOWNER_EMAIL=replace-me
Frontend flags:
PUBLIC_ENABLE_MOBILE_CTA_BUTTON=falsekeeps the sticky mobile booking CTA hidden.- Set
PUBLIC_ENABLE_MOBILE_CTA_BUTTON=trueto show it again.
- Confirm the shared Docker network already exists:
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 -ExecutionPolicy Bypass -File .\deploy.ps1
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.
Or skip the confirmation prompt:
powershell -ExecutionPolicy Bypass -File .\deploy.ps1 -Force
To rebuild and restart only one service, for example the mail API:
powershell -ExecutionPolicy Bypass -File .\deploy.ps1 -Force -Service mail-api
Remote Git deploy
If you want the production server to pull straight from Gitea instead of receiving an uploaded tarball from Windows, use scripts/deploy-from-git.sh on the server.
Recommended credential setup for a private HTTPS repo:
umask 077
cat > ~/.netrc <<'EOF'
machine g.sublogue.com
login YOUR_GITEA_USERNAME
password YOUR_READ_ONLY_TOKEN
EOF
chmod 600 ~/.netrc
Install the script on the server and make it executable:
install -m 0755 scripts/deploy-from-git.sh /usr/local/bin/goodwalk-deploy
The remote host must have git and docker. A host-level node install is
optional; if it is missing, the script will export homepage content using a
temporary node:22-alpine container instead.
Run a full deploy from the repo:
/usr/local/bin/goodwalk-deploy \
--repo-url https://g.sublogue.com/admin/gw-svelte.git \
--branch main \
--deploy-path /docker/goodwalk-svelte \
--compose-file docker-compose.prod.yml \
--project-name goodwalk-svelte \
--nginx-source nginx/goodwalk.co.nz.svelte.conf.example \
--nginx-target /docker/nginx/conf.d/goodwalk.co.nz.conf \
--nginx-compose-file /docker/nginx/docker-compose.yml \
--nginx-project-name nginx \
--maintenance-host-dir /docker/nginx/maintenance \
--maintenance-flag /docker/nginx/conf.d/maintenance.flag
Deploy a specific commit or tag:
/usr/local/bin/goodwalk-deploy \
--repo-url https://g.sublogue.com/admin/gw-svelte.git \
--branch main \
--ref <commit-or-tag> \
--deploy-path /docker/goodwalk-svelte \
--compose-file docker-compose.prod.yml \
--project-name goodwalk-svelte \
--nginx-source nginx/goodwalk.co.nz.svelte.conf.example \
--nginx-target /docker/nginx/conf.d/goodwalk.co.nz.conf \
--nginx-compose-file /docker/nginx/docker-compose.yml \
--nginx-project-name nginx \
--maintenance-host-dir /docker/nginx/maintenance \
--maintenance-flag /docker/nginx/conf.d/maintenance.flag
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:
deploy.ps1exports the currentsrc/lib/content/homepage.tsintodeploy-data/homepage-content.json.- The deploy archive uploads that JSON payload with the app source.
- After the Goodwalk stack is updated, the remote helper runs a content sync inside the app container.
- That sync upserts the
homepagerow insite_content.
This means future deploys will carry your latest file-based homepage/navigation/ shared content changes into production PostgreSQL automatically.
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:
/docker/nginx/conf.d/goodwalk.co.nz.conf
Use the repo example as the new target config:
nginx/goodwalk.co.nz.svelte.conf.example
Important:
deploy.ps1now copies the repo nginx config to/docker/nginx/conf.d/goodwalk.co.nz.confand 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.
Manual nginx commands, if you ever need them:
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.ymlto 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
.envfile. - The site check in
deploy.ps1targetshttps://www.goodwalk.co.nz/api/health. Before nginx cutover, use-SkipSiteCheckor expect that check to fail.