Ticketing Server Deployment
Environment Variables
Server configuration is done entirely through environment variables. Keys are case-insensitive, an empty value counts as unset, and flag-style values are only true for 1/true/yes/on. The Docker image is scratch-based, so config is injected purely through environment variables with no config file.
| Environment variable | Default | Description |
|---|---|---|
PORT | 5225 | Listen port in single mode. Ignored in cluster mode, where the port from CLUSTER_SELF is used instead |
SOCKET_BIND | 0.0.0.0 | Listen address |
CLIENT_TOKENS | (none) | Comma-separated list of client auth tokens. If unset, an empty token is allowed |
CLUSTER_SELF | (none) | This node's advertised address, host:port. Presence enables cluster mode |
CLUSTER_PEERS | (none) | Comma-separated list of every node's advertised address. Exactly 3 or 5, same order on every node, must include self |
CLUSTER_TOKENS | (none) | Comma-separated list of peer-to-peer auth tokens |
TLS_CERT / TLS_KEY | (none) | Paths to the certificate/private key PEM files — set both together to enable TLS |
TLS_CA | (none) | CA for verifying peer certificates — falls back to the system trust store if unset |
TLS_SKIP_VERIFY | false | Skip peer certificate verification — test only |
DOCKER | false | Pins the internal listen ports to 5225/6225 in cluster mode — set by default in the official image |
DEBUG | Depends on build | 1/true enables debug logging |
For the complete rules (port derivation, advertised addresses, zero-downtime token rotation, etc.) see Configuration (Env Vars).
Single vs. Cluster
- Single (1 node): without
CLUSTER_*, the node runs standalone. Fastest, but there's a brief blip on restart, and state is in-memory so it's lost on restart (leaseis the safety net). - Cluster (non-stop): 3 or 5 nodes. The nodes share a single lock state via Raft consensus — only the leader handles requests, and every state change is only finalized after a majority commit. If the leader dies, a new one is elected within 0.5-1 second, and as long as a majority stays alive, the lock state and service keep running. See How It Works and Protocol (Cluster) for the full explanation.
- Each cluster node listens on the client port plus a Raft port (client port + 1000) — open both ports in your firewall.
Kubernetes cluster deployments use a StatefulSet + headless Service. Give each pod a stable DNS name (
ticketing-0.ticketing…,ticketing-1.ticketing…), put those names intoCLUSTER_PEERS, and auto-inject each pod'sCLUSTER_SELFvia the downward API (metadata.name).
Non-Stop Restart (Rolling)
There's one core rule — a majority must always stay alive (at most 1 of 3 down at a time).
- Restart followers first, one at a time. Once a restarted node catches up via replication or a snapshot, move on to the next.
- Finally, take down the leader — a new one is elected within 0.5-1 second, and clients switch over transparently.
- Bringing a downed node back up rejoins it as a follower.
See Rolling Restart Procedure for the detailed steps.
Reference
- Image:
sarolab/ticketing· Github: saro-lab/ticketing - Register every node address with the client (broker) so it fails over automatically. See Libraries for language-specific usage.