Running as a Service
For production, run scd-server as a system service so it starts automatically on reboot and restarts if it crashes.
Default setup
Most teams run scd-server on a dedicated machine (or a VM) that is always on and reachable by all developers on the same network. With host: 0.0.0.0 in config.yml, developers connect directly using the server's LAN IP — no extra networking required.
If all developers are on the same machine as scd-server, use host: 127.0.0.1 instead.
Linux — systemd
systemd is the recommended service manager on Linux. The unit below is production-ready: dedicated system user, filesystem hardening, and automatic restart.
1. Create a system user
sudo useradd --system --no-create-home --shell /bin/false scd-server
2. Place the binary
sudo mkdir -p /opt/scd-server/data
sudo cp scd-server-linux-x64 /opt/scd-server/
There are no license files to copy — scd-server is licensed with a key (see step 5), and everything it needs to validate that license is built into the binary.
3. Run init and edit config
Run --init in the terminal — it sets up config.yml and prompts you to set the admin password. The service in step 6 won't start until an admin account exists, so set it here first.
sudo /opt/scd-server/scd-server-linux-x64 --init
sudo nano /opt/scd-server/config.yml
Provisioning without an interactive terminal? Provide the password via the environment for the --init run instead: sudo SCD_ADMIN_PASSWORD='<strong-password>' /opt/scd-server/scd-server-linux-x64 --init. See First-run setup for details.
4. Set ownership and permissions
sudo chown -R scd-server:scd-server /opt/scd-server
sudo chmod 700 /opt/scd-server/data
The data/ directory holds scd-server's private state — its database and cached license. Keep it readable only by the service account (the chmod 700 above ensures this) and treat it as sensitive: include it in your backups and restrict access to trusted administrators.
5. Create the service unit
Create /etc/systemd/system/scd-server.service:
[Unit]
Description=Secure Code by Design Server
Documentation=https://docs-server.securecodebydesign.com
After=network.target
[Service]
Type=simple
User=scd-server
Group=scd-server
WorkingDirectory=/opt/scd-server
ExecStart=/opt/scd-server/scd-server-linux-x64
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=scd-server
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/opt/scd-server/data
[Install]
WantedBy=multi-user.target
6. Enable and start
sudo systemctl daemon-reload
sudo systemctl enable scd-server
sudo systemctl start scd-server
sudo systemctl status scd-server
View logs
sudo journalctl -u scd-server -f # live
sudo journalctl -u scd-server -n 100 # recent
7. Activate the license
Once the service is running, sign in to the dashboard and paste your scd-lic-… key under
Admin → Settings → License. To provision it without the UI, add the key to the service
environment instead — in the [Service] block of the unit:
Environment=SCD_LICENSE_KEY=scd-lic-…
then sudo systemctl daemon-reload && sudo systemctl restart scd-server. See
License for the lease model and offline activation.
Windows — NSSM
NSSM (Non-Sucking Service Manager) is the recommended way to run scd-server as a Windows service.
1. Install NSSM
Download NSSM from nssm.cc and place nssm.exe somewhere on your PATH (e.g. C:\Windows\System32).
2. Install the service
Open a command prompt as Administrator:
nssm install scd-server "C:\scd-server\scd-server-win-x64.exe"
nssm set scd-server AppDirectory "C:\scd-server"
nssm set scd-server DisplayName "Secure Code by Design Server"
nssm set scd-server Start SERVICE_AUTO_START
nssm start scd-server
Useful NSSM commands
nssm status scd-server
nssm restart scd-server
nssm stop scd-server
nssm remove scd-server confirm
Alternative: Windows Task Scheduler
- Open Task Scheduler → Create Task
- General: name it
scd-server, select "Run whether user is logged on or not" - Triggers: New → At startup
- Actions: New → Start a program →
C:\scd-server\scd-server-win-x64.exe, Start in:C:\scd-server - Settings: check "Restart if the task fails"
Task Scheduler gives less control over logging and restart behaviour than NSSM.
macOS
macOS service management for scd-server is on the roadmap. For now, run scd-server from a terminal session.
Advanced configuration
Bind to a specific address
To bind to a specific LAN or VPN interface instead of all interfaces:
host: 192.168.1.50
port: 3000
Reverse proxy and TLS
scd-server does not handle TLS directly. Terminate TLS with nginx or Caddy and proxy to localhost.
nginx example:
server {
listen 443 ssl;
server_name scd.example.internal;
ssl_certificate /etc/ssl/scd.example.internal.crt;
ssl_certificate_key /etc/ssl/scd.example.internal.key;
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;
}
}
Set host: 127.0.0.1 in config.yml so scd-server only accepts proxied connections.
Tell scd-server it's behind a proxy. When — and only when — a trusted reverse proxy sits in front, set SCD_TRUST_PROXY in the service environment (systemd: add Environment=SCD_TRUST_PROXY=1 to the [Service] block; 1 trusts a single proxy hop). With it set, scd-server reads the real client from X-Forwarded-For and the transport from X-Forwarded-Proto, which makes three things correct:
- abuse protections see the real client address rather than the proxy's;
- the session cookie's
Secureflag is set automatically over HTTPS; - HSTS is sent automatically over HTTPS.
Do not set SCD_TRUST_PROXY when scd-server is exposed directly (no proxy in front). With no trusted proxy to set it, the forwarded address can be supplied by the caller, so leaving it unset is the correct, safe default for direct exposure.
Remote access
scd-server is an internal tool — the whole point is that your code never leaves your network. The recommended way to make it reachable for developers off-site is a VPN, not an open internet port.
Tailscale is the simplest option: install Tailscale on the scd-server machine and on each developer machine. Each gets a stable Tailscale IP. Developers use that IP as --central-url in scd configure.
WireGuard is a self-hosted, open-source VPN with no coordination server.
Headscale is a self-hosted Tailscale coordination server — full control, no third-party service.
If host in config.yml is set to a VPN-assigned address (e.g. a Tailscale IP), the scd-server service must start after the VPN interface is up. On Linux, add the VPN service to the After= line in the systemd unit:
# Tailscale
After=network.target tailscaled.service
# WireGuard (e.g. wg0)
After=network.target wg-quick@wg0.service
Without this, scd-server may fail to bind on boot if the interface is not ready yet.