Deployment
Docker
The recommended way to deploy Substrukt in production. A pre-built image is available on the GitHub Container Registry.
Pull and run
docker pull ghcr.io/wavefunk/substrukt
docker run -p 3000:3000 -v substrukt-data:/data ghcr.io/wavefunk/substrukt
Build from source
docker build -t substrukt .
The Dockerfile uses a multi-stage build:
- Builder stage: Compiles the Rust binary with
cargo build --releaseusing a nightly Rust image. Dependencies are cached separately for faster rebuilds. - Runtime stage: Copies the binary and templates into a minimal Debian image with only
ca-certificatesinstalled.
All persistent data is stored in the /data volume:
substrukt.db-- users, sessions, API tokensaudit.db-- audit logschemas/-- JSON Schema filescontent/-- content entriesuploads/-- uploaded files
Configuration
Override defaults with command arguments:
docker run -p 8080:8080 \
-v substrukt-data:/data \
substrukt serve \
--data-dir /data \
--port 8080 \
--secure-cookies
Docker Compose example
services:
substrukt:
image: ghcr.io/wavefunk/substrukt
ports:
- "3000:3000"
volumes:
- substrukt-data:/data
command: >
serve
--data-dir /data
--db-path /data/substrukt.db
--port 3000
--secure-cookies
restart: unless-stopped
volumes:
substrukt-data:
Binary deployment
For environments without Docker:
cargo build --release
Copy the binary and templates directory to your server:
/opt/substrukt/
substrukt # binary
templates/ # template files
Run it:
/opt/substrukt/substrukt serve \
--data-dir /var/lib/substrukt \
--secure-cookies
Systemd service
[Unit]
Description=Substrukt CMS
After=network.target
[Service]
Type=simple
User=substrukt
ExecStart=/opt/substrukt/substrukt serve \
--data-dir /var/lib/substrukt \
--secure-cookies
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Reverse proxy
Substrukt listens on 0.0.0.0:3000 by default. In production, place it behind a reverse proxy for TLS termination.
Nginx example
server {
listen 443 ssl;
server_name cms.example.com;
ssl_certificate /etc/letsencrypt/live/cms.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cms.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
}
}
When using HTTPS, pass --secure-cookies to Substrukt so that session cookies have the Secure flag set.
Production checklist
- [ ] Use
--secure-cookieswhen behind HTTPS - [ ] Set
X-Forwarded-Forheader in your reverse proxy and use--trust-proxy-headers(used for rate limiting) - [ ] Mount persistent storage for
/data(Docker) or--data-dir(binary) - [ ] Back up
substrukt.dbregularly (contains users and tokens), or configure S3 backups via the UI - [ ] Set
RUST_LOG=substrukt=infofor production logging level - [ ] Configure deployment targets in the UI if using the deploy workflow