Skip to main content

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
Unattended install

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
  1. Open Task Scheduler → Create Task
  2. General: name it scd-server, select "Run whether user is logged on or not"
  3. Triggers: New → At startup
  4. Actions: New → Start a program → C:\scd-server\scd-server-win-x64.exe, Start in: C:\scd-server
  5. 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 Secure flag is set automatically over HTTPS;
  • HSTS is sent automatically over HTTPS.
Don't set it without a proxy

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.

Boot ordering when binding to a VPN address

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.