Deployment
This guide covers deploying Swytch in production environments, including container images, systemd services, and Kubernetes.
Official Docker images are available on GitHub Container Registry:
# Latest stable release
docker pull ghcr.io/bottledcode/swytch:latest
# Specific version
docker pull ghcr.io/bottledcode/swytch:1.0.0
# Redis mode (in-memory)
docker run -d \
--name swytch-redis \
-p 6379:6379 \
ghcr.io/bottledcode/swytch:latest \
redis --bind 0.0.0.0 --maxmemory 4gb
# Redis mode (persistent)
docker run -d \
--name swytch-redis \
-p 6379:6379 \
-v /data/swytch:/data \
ghcr.io/bottledcode/swytch:latest \
redis --persistent --db-path /data/redis.db --bind 0.0.0.0 --maxmemory 4gb
# Memcached mode
docker run -d \
--name swytch-memcached \
-p 11211:11211 \
ghcr.io/bottledcode/swytch:latest \
memcached -l 0.0.0.0 -m 4096
services:
swytch:
image: ghcr.io/bottledcode/swytch:latest
command: redis --persistent --db-path /data/redis.db --bind 0.0.0.0 --maxmemory 4gb --metrics-port 9090
ports:
- "6379:6379"
- "9090:9090"
volumes:
- swytch-data:/data
deploy:
resources:
limits:
memory: 5G
reservations:
memory: 4G
restart: unless-stopped
volumes:
swytch-data:
To enable password authentication, use --requirepass. Since the container image is built FROM scratch (no shell),
you cannot use shell variable expansion. Instead:
Docker Compose: Use host environment variable interpolation:
services:
swytch:
image: ghcr.io/bottledcode/swytch:latest
command:
- redis
- --bind=0.0.0.0
- --requirepass=${REDIS_PASSWORD}
ports:
- "6379:6379"
Set REDIS_PASSWORD in your environment or .env file before running docker compose up.
Kubernetes: Use a Secret with environment variable substitution:
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: swytch-secrets
key: password
args:
- redis
- --bind=0.0.0.0
- --requirepass=$(REDIS_PASSWORD)
Kubernetes substitutes $(REDIS_PASSWORD) at pod creation time—no shell required.
For bare-metal or VM deployments, use systemd to manage Swytch as a service.
Create /etc/systemd/system/swytch.service:
[Unit]
Description=Swytch Redis-compatible cache server
Documentation=https://bottledcode.github.io/swytch/
After=network.target
[Service]
Type=simple
User=swytch
Group=swytch
ExecStart=/usr/local/bin/swytch redis \
--persistent \
--db-path /var/lib/swytch/redis.db \
--bind 127.0.0.1 \
--maxmemory 4gb \
--metrics-port 9090
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/swytch
PrivateTmp=true
# Resource limits
LimitNOFILE=65535
MemoryMax=5G
[Install]
WantedBy=multi-user.target
# Create service user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin swytch
# Create data directory
sudo mkdir -p /var/lib/swytch
sudo chown swytch:swytch /var/lib/swytch
# Install binary
sudo cp swytch /usr/local/bin/
sudo chmod +x /usr/local/bin/swytch
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable swytch
sudo systemctl start swytch
# Check status
sudo systemctl status swytch
sudo journalctl -u swytch -f
Example Kubernetes manifests are shown below. Adapt them to your environment.
For persistent deployments, use a StatefulSet with a PersistentVolumeClaim:
apiVersion: v1
kind: Service
metadata:
name: swytch
labels:
app: swytch
spec:
ports:
- port: 6379
name: redis
- port: 9090
name: metrics
clusterIP: None
selector:
app: swytch
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: swytch
spec:
serviceName: swytch
replicas: 1
selector:
matchLabels:
app: swytch
template:
metadata:
labels:
app: swytch
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
terminationGracePeriodSeconds: 30
containers:
- name: swytch
image: ghcr.io/bottledcode/swytch:latest
args:
- redis
- --persistent
- --db-path=/data/redis.db
- --bind=0.0.0.0
- --maxmemory=4gb
- --metrics-port=9090
ports:
- containerPort: 6379
name: redis
- containerPort: 9090
name: metrics
resources:
requests:
memory: "4Gi"
cpu: "1"
limits:
memory: "5Gi"
cpu: "4"
volumeMounts:
- name: data
mountPath: /data
readinessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 15
periodSeconds: 20
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: fast-ssd # Use your NVMe/SSD storage class
resources:
requests:
storage: 100Gi
Set resource limits higher than --maxmemory to account for:
- Go runtime overhead (~100–200MB)
- Connection buffers (~1KB per client)
- Temporary allocations during operations
Rule of thumb: Set container/process memory limit to maxmemory + 20% or maxmemory + 512MB, whichever is larger.
| maxmemory | Recommended limit |
|---|---|
| 1GB | 1.5GB |
| 4GB | 5GB |
| 16GB | 19GB |
| 64GB | 77GB |
Swytch scales well with multiple cores. For sizing:
- Light workloads (<50k ops/sec): 1–2 cores
- Moderate workloads (50k-200k ops/sec): 2–4 cores
- Heavy workloads (>200k ops/sec): 4–8 cores
Set CPU requests to your expected baseline and limits higher to handle spikes.
NoteGOMAXPROCS in containers: The--threadsflag setsGOMAXPROCS. Go’s runtime doesn’t always correctly detect cgroup CPU limits in containers. If you set a CPU limit (e.g.,cpu: "2"), explicitly set--threads=2to match. Otherwise, Go may spawn more OS threads than your limit allows, causing throttling.
For persistent mode:
- Use NVMe or SSD storage classes (not spinning disks)
- Provision 2-3x your expected data size to allow for growth and defragmentation headroom
- Monitor disk usage—unlike memory, disk is not bounded by
--maxmemory
Each client connection uses a file descriptor. Set LimitNOFILE (systemd) or container ulimits appropriately:
# Kubernetes
securityContext:
capabilities:
add: [ "SYS_RESOURCE" ]
# Or set in the container runtime
For systemd, LimitNOFILE=65535 is usually sufficient.
The simplest and most reliable health check is a TCP connection to the Redis port:
readinessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 15
periodSeconds: 20
This is the recommended approach because the Swytch container image is built FROM scratch and does not include
redis-cli or any other tools.
For more thorough checks using redis-cli, you need a sidecar container or to build a custom image that includes it:
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
periodSeconds: 10
If metrics are enabled, check the /metrics endpoint:
livenessProbe:
httpGet:
path: /metrics
port: 9090
initialDelaySeconds: 10
periodSeconds: 30
Swytch handles SIGTERM gracefully: it stops accepting new connections, completes in-flight requests, flushes pending
writes (in persistent mode), and exits cleanly. The terminationGracePeriodSeconds in the StatefulSet example (30s) is
typically enough.
A preStop hook is not required. Kubernetes sends SIGTERM directly to the container, and Swytch handles it
correctly. The FROM scratch image contains only the Swytch binary (no shell), so shell-based preStop hooks won’t work
without a custom image.