Docker Deployment
Deploy Capyshop in productie met Docker Compose, de voorgebouwde containerimage en PostgreSQL met pgvector.
Vereisten
- Docker Engine 20+
- Docker Compose v2+
1. Docker Compose Bestand Aanmaken
Maak een docker-compose.yml in je deployment-directory:
services:
postgres:
image: pgvector/pgvector:pg17
restart: always
environment:
POSTGRES_USER: capyshop
POSTGRES_PASSWORD: changeme
POSTGRES_DB: capyshop
volumes:
- postgres_data:/var/lib/postgresql/data
app:
image: capyshop/capyshop:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://capyshop:changeme@postgres:5432/capyshop
- BETTER_AUTH_SECRET=<generate-with-openssl-rand-base64-32>
- MASTER_SECRET=<generate-with-openssl-rand-base64-32>
- TRUSTED_ORIGINS=https://mystore.com
- BASE_URL=https://mystore.com
volumes:
- ./data:/app/data
depends_on:
- postgres
volumes:
postgres_data:Vervang de voorbeeldwaarden:
| Variable | Beschrijving |
|---|---|
BETTER_AUTH_SECRET | Geheime sleutel voor sessie-ondertekening. Genereer met openssl rand -base64 32. |
MASTER_SECRET | Applicatie-hoofdgeheim voor versleuteling. Genereer met openssl rand -base64 32. |
POSTGRES_USER | PostgreSQL-gebruikersnaam (moet overeenkomen in postgres en DATABASE_URL). |
POSTGRES_PASSWORD | PostgreSQL-wachtwoord (moet overeenkomen in postgres en DATABASE_URL). |
POSTGRES_DB | PostgreSQL-databasenaam (moet overeenkomen in postgres en DATABASE_URL). |
TRUSTED_ORIGINS | Kommagescheiden lijst van vertrouwde origins voor CSRF-bescherming en OAuth-redirects (bijv. https://mijnwinkel.nl). |
BASE_URL | Publieke URL van de winkel, gebruikt in e-mails, SEO, sitemaps (bijv. https://mijnwinkel.nl). |
Optioneel: SMTP-configuratie via Omgevingsvariabelen
Standaard worden SMTP-gegevens beheerd via Instellingen → E-mail in het admin-paneel. Als je SMTP liever bij het uitrollen wilt configureren, kun je de onderstaande omgevingsvariabelen instellen. Wanneer ze zijn ingesteld, overschrijven ze de waarden uit het admin-paneel en worden de bijbehorende velden in het E-mailformulier uitgeschakeld.
environment:
# ...andere variabelen
- SMTP_HOST=smtp.sendgrid.net
- SMTP_PORT=587
- SMTP_USER=apikey
- SMTP_PASSWORD=jouw-smtp-wachtwoord| Variable | Beschrijving |
|---|---|
SMTP_HOST | Hostnaam van de mailserver (bijv. smtp.sendgrid.net). |
SMTP_PORT | Poort van de mailserver. Meestal 587 voor TLS of 465 voor SSL. Moet een geheel getal in [1, 65535] zijn. |
SMTP_USER | SMTP-inlognaam. |
SMTP_PASSWORD | SMTP-inlogwachtwoord. Wordt als platte tekst in de omgeving opgeslagen — gaat niet door de versleutelingslaag van de database. |
Elke variabele is onafhankelijk. Als alleen SMTP_HOST is ingesteld, blijven de andere drie SMTP-velden bewerkbaar in de admin-interface en worden ze uit de database gelezen.
Optioneel: S3-asset-opslag
Standaard worden geuploade afbeeldingen en andere assets lokaal op schijf opgeslagen onder data/files/. Om uploads naar een S3-compatibele bucket te sturen en vanaf een CDN te serveren, zet je de opslagmodus op s3 en configureer je de bucket-credentials.
environment:
# ...andere vars
- ASSETS_STORAGE_MODE=s3
- ASSETS_S3_ENDPOINT=https://s3.amazonaws.com
- ASSETS_S3_REGION=us-east-1
- ASSETS_S3_BUCKET=my-store-assets
- ASSETS_S3_ACCESS_KEY_ID=AKIA...
- ASSETS_S3_SECRET_ACCESS_KEY=...
- ASSETS_PUBLIC_BASE_URL=https://cdn.mystore.com
- ASSETS_MAX_BYTES=10gb| Variabele | Beschrijving |
|---|---|
ASSETS_STORAGE_MODE | local (standaard) of s3. Bij s3 zijn de zes ASSETS_S3_*-variabelen en ASSETS_PUBLIC_BASE_URL verplicht. |
ASSETS_S3_ENDPOINT | S3-API-endpoint (bijv. https://s3.amazonaws.com, of de URL van je provider voor Cloudflare R2, Backblaze B2, MinIO, enz.). |
ASSETS_S3_REGION | Bucket-regio (bijv. us-east-1, auto voor R2). |
ASSETS_S3_BUCKET | Bucketnaam. |
ASSETS_S3_ACCESS_KEY_ID | Access key met lees-/schrijfrechten op de bucket. |
ASSETS_S3_SECRET_ACCESS_KEY | Secret access key. |
ASSETS_PUBLIC_BASE_URL | Publieke base-URL die de storefront gebruikt om assets te laden (bijv. je CDN-domein dat naar de bucket wijst). |
ASSETS_MAX_BYTES | Optionele cumulatieve opslaglimiet over alle bestanden. Accepteert 10gb, 500mb of een ruwe byte-telling. Geldt zowel in local- als in s3-modus. Limieten per upload (5 MB afbeelding / 50 MB video) blijven ongewijzigd. |
Bij het migreren van een bestaande winkel van local naar s3 voer je het meegeleverde migratiescript een keer uit binnen een uitgerolde container om de bestaande bestanden naar de bucket te uploaden en de WebP-varianten voor te genereren:
docker exec -it <container> node build/scripts/migrate-assets-to-s3.mjsHet script is idempotent — het opnieuw uitvoeren is veilig en het slaat werk over dat al gedaan is. Als je bestaande originelen op de host staan (bijvoorbeeld /docker/<store>/app_data/files/), bind-mount dat pad dan op /app/data/files binnen de container zodat het script ze kan oppakken; anders valt het terug op wat al in de bucket staat.
2. De Applicatie Starten
docker compose up -dDit start twee services:
- postgres — PostgreSQL 17 met pgvector (
pgvector/pgvector:pg17), luisterend op poort5432. - app — De applicatiecontainer (
capyshop/capyshop:latest), beschikbaar op poort3000.
Bij het opstarten voert de applicatiecontainer automatisch databasemigraties uit (prisma migrate deploy) voordat de server wordt gestart.
3. Verifieren
# Controleer of beide containers draaien
docker compose ps
# Bekijk applicatielogs
docker compose logs appDe applicatie zou bereikbaar moeten zijn op http://<jouw-host>:3000.
Reverse Proxy
Voor productie-deployments met HTTPS, plaats een reverse proxy (zoals Traefik, Caddy of nginx) voor de applicatie. De reverse proxy verzorgt TLS-terminatie en stuurt verkeer door naar poort 3000.
Gzip-compressie Inschakelen met Traefik
Als je Traefik als reverse proxy gebruikt, schakel dan gzip-compressie in om de grootte van responses te verkleinen en de laadsnelheid te verbeteren — een factor in de ranking van zoekmachines. Voeg de volgende labels toe aan de app-service in je docker-compose.yml:
labels:
- "traefik.http.middlewares.compress.compress.minresponsebodybytes=256"
- "traefik.http.routers.STORE_NAME-app.middlewares=compress"Vervang STORE_NAME door de naam van de Traefik-router van je winkel. Het eerste label definieert een compress-middleware die gzip toepast op alle responses groter dan 256 bytes. Het tweede label koppelt die middleware aan de router van je winkel.
Volumes
| Volume / Mount | Doel |
|---|---|
postgres_data | Bewaart PostgreSQL-gegevens bij het herstarten van containers. |
./data:/app/data | Bewaart geuploade bestanden en applicatiegegevens bij herstarten. |
Bouwen vanuit Broncode
Als je de image liever zelf bouwt in plaats van de voorgebouwde versie te gebruiken:
docker build -t capyshop .De Dockerfile gebruikt een multi-stage build:
- Installeert alle afhankelijkheden en bouwt de applicatie.
- Kopieert alleen productie-afhankelijkheden en de build-uitvoer naar de uiteindelijke image.
- Voert
prisma migrate deployuit en start vervolgens de server op poort3000.
Om je eigen image te gebruiken, werk het image-veld bij in je docker-compose.yml, of draai standalone:
docker run -p 3000:3000 \
-e DATABASE_URL=postgresql://user:pass@host:5432/db \
-e BETTER_AUTH_SECRET=your-secret \
-e MASTER_SECRET=your-secret \
capyshopProbleemoplossing
| Fout | Oorzaak | Oplossing |
|---|---|---|
app stopt direct | Ontbrekende of ongeldige omgevingsvariabelen | Controleer docker compose logs app en verifieer alle vereiste omgevingsvariabelen |
ECONNREFUSED naar postgres | App gestart voordat de database gereed was | Herstart de app: docker compose restart app |
P3009 - failed migrations | Een eerdere migratie heeft een vervuilde staat gelaten | DROP TABLE IF EXISTS _prisma_migrations CASCADE; in de database, herstart daarna |
type "vector" does not exist | pgvector-extensie niet aangemaakt | De pgvector/pgvector:pg17-image bevat deze, maar voer CREATE EXTENSION IF NOT EXISTS vector; uit bij gebruik van een andere image |
Poort 3000 al in gebruik | Een ander proces gebruikt de poort | Stop het conflicterende proces of wijzig de poortmapping in docker-compose.yml |
| Geuploade bestanden weg na herstart | ./data-volume niet gekoppeld | Zorg ervoor dat de volumes-sectie ./data:/app/data bevat |