Deployment¶
Burrow is designed for standalone deployment — it handles TLS termination, ACME certificates, and graceful restarts without requiring a reverse proxy.
Graceful Restart (SIGHUP)¶
On Linux and macOS, the server supports zero-downtime restarts via SIGHUP. When a SIGHUP signal is received, the server:
- Spawns a new child process that inherits the listener file descriptors
- The child starts accepting connections
- The parent stops accepting new connections and drains in-flight requests
- The parent exits
This allows binary upgrades and configuration changes without dropping any connections.
Note
The parent process exits after the child takes over. If you start the server from a terminal, the shell prompt returns while the child continues running in the background. Use a PID file or process manager (systemd, supervisor) to manage the server lifecycle.
PID File¶
Use --pid-file to write the server's PID to a file. The PID file is updated on each restart so it always points to the current active process.
When Graceful Restart Is Disabled¶
The server automatically falls back to simple mode (no graceful restart, only graceful shutdown) in these situations:
- Windows —
SIGHUPand file descriptor inheritance are not available - PID 1 (containers) — the parent exiting would stop the container (see Docker below)
- Concurrent upgrader — if tableflip cannot initialize (e.g. in tests)
In simple mode, SIGINT and SIGTERM still trigger a graceful shutdown that drains in-flight requests.
systemd¶
Use Type=forking so systemd tracks the new process after a graceful restart. The PIDFile directive tells systemd where to find the current PID.
[Unit]
Description=My Burrow App
After=network.target
[Service]
Type=forking
PIDFile=/run/myapp/server.pid
ExecStart=/usr/local/bin/server --pid-file /run/myapp/server.pid
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
# Recommended hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp /run/myapp
[Install]
WantedBy=multi-user.target
With this setup:
systemctl start myapp— starts the serversystemctl reload myapp— sends SIGHUP, triggers zero-downtime restartsystemctl stop myapp— sends SIGTERM, triggers graceful shutdownsystemctl restart myapp— stop + start (brief downtime)
Tip
Always prefer systemctl reload over systemctl restart for configuration changes and binary upgrades.
Docker¶
In a Docker container, the server runs as PID 1. Graceful restart is automatically disabled because the parent exiting would cause the container runtime to stop the container.
The server still supports graceful shutdown — when Docker sends SIGTERM (via docker stop), in-flight requests are drained before exit.
FROM golang:1.25 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /server ./cmd/server
FROM gcr.io/distroless/static-debian12
COPY --from=builder /server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
For zero-downtime deployments with Docker, use an orchestrator-level rolling restart (e.g. docker compose up -d --force-recreate) rather than in-process restarts.
Bare Metal / VPS¶
For a simple standalone deployment without a process manager:
# Build
go build -o server ./cmd/server
# Run with PID file for restart support
./server --pid-file ./server.pid &
# Graceful restart (e.g. after deploying a new binary)
kill -HUP $(cat ./server.pid)
# Graceful shutdown
kill $(cat ./server.pid)
For production use, a process manager like systemd is recommended to handle automatic restarts on failure and log management.
TLS¶
See the TLS guide for full details on ACME, self-signed, manual, and auto TLS modes.