Deployment¶
Guide for deploying Signal Fish Server in production.
Docker Deployment¶
Pull and Run¶
Bash
docker pull ghcr.io/ambiguous-interactive/signal-fish-server:latest
docker run -d \
--name signal-fish \
-p 3536:3536 \
-v ./config.json:/app/config.json:ro \
ghcr.io/ambiguous-interactive/signal-fish-server:latest
Custom Config¶
Mount your config file:
Bash
docker run -d \
-p 3536:3536 \
-v ./config.json:/app/config.json:ro \
-v ./logs:/app/logs \
ghcr.io/ambiguous-interactive/signal-fish-server:latest
Environment Variables¶
Bash
docker run -d \
-p 3536:3536 \
-e SIGNAL_FISH_PORT=8080 \
-e SIGNAL_FISH_SERVER__DEFAULT_MAX_PLAYERS=16 \
-e SIGNAL_FISH_SECURITY__REQUIRE_WEBSOCKET_AUTH=true \
ghcr.io/ambiguous-interactive/signal-fish-server:latest
Docker Compose¶
YAML
services:
signal-fish:
image: ghcr.io/ambiguous-interactive/signal-fish-server:latest
ports:
- "3536:3536"
volumes:
- ./config.json:/app/config.json:ro
- ./logs:/app/logs
environment:
- RUST_LOG=info
- SIGNAL_FISH_SECURITY__REQUIRE_WEBSOCKET_AUTH=true
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3536/v2/health"]
interval: 30s
timeout: 10s
retries: 3
Production Configuration¶
JSON
{
"port": 3536,
"server": {
"default_max_players": 8,
"ping_timeout": 30,
"room_cleanup_interval": 60,
"max_rooms_per_game": 1000,
"empty_room_timeout": 180,
"inactive_room_timeout": 1800,
"reconnection_window": 300,
"enable_reconnection": true
},
"rate_limit": {
"max_room_creations": 5,
"time_window": 60,
"max_join_attempts": 20
},
"logging": {
"dir": "logs",
"enable_file_logging": true,
"rotation": "daily",
"format": "Json"
},
"security": {
"cors_origins": "https://yourgame.com",
"require_websocket_auth": true,
"max_message_size": 65536,
"max_connections_per_ip": 10
},
"websocket": {
"enable_batching": true,
"batch_size": 10,
"batch_interval_ms": 16
}
}
Reverse Proxy Setup¶
nginx¶
Nginx Configuration File
upstream signal_fish {
server 127.0.0.1:3536;
}
server {
listen 443 ssl http2;
server_name signal.yourgame.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /v2/ws {
proxy_pass http://signal_fish;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket timeouts
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
location /v2/health {
proxy_pass http://signal_fish;
proxy_set_header Host $host;
}
location /metrics {
proxy_pass http://signal_fish;
proxy_set_header Host $host;
# Optional: restrict metrics access
allow 10.0.0.0/8;
deny all;
}
}
Caddy¶
Text Only
signal.yourgame.com {
reverse_proxy /v2/ws localhost:3536 {
header_up X-Real-IP {remote_host}
}
reverse_proxy /v2/health localhost:3536
reverse_proxy /metrics localhost:3536
}
Cloud Providers¶
AWS (ECS Fargate)¶
JSON
{
"family": "signal-fish-server",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "signal-fish",
"image": "ghcr.io/ambiguous-interactive/signal-fish-server:latest",
"portMappings": [
{
"containerPort": 3536,
"protocol": "tcp"
}
],
"environment": [
{
"name": "SIGNAL_FISH_SECURITY__REQUIRE_WEBSOCKET_AUTH",
"value": "true"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/signal-fish",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
Google Cloud Run¶
Bash
gcloud run deploy signal-fish \
--image ghcr.io/ambiguous-interactive/signal-fish-server:latest \
--platform managed \
--region us-central1 \
--port 3536 \
--set-env-vars SIGNAL_FISH_SECURITY__REQUIRE_WEBSOCKET_AUTH=true \
--allow-unauthenticated \
--max-instances 10
Kubernetes¶
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: signal-fish-server
spec:
replicas: 3
selector:
matchLabels:
app: signal-fish
template:
metadata:
labels:
app: signal-fish
spec:
containers:
- name: signal-fish
image: ghcr.io/ambiguous-interactive/signal-fish-server:latest
ports:
- containerPort: 3536
env:
- name: SIGNAL_FISH_SECURITY__REQUIRE_WEBSOCKET_AUTH
value: "true"
volumeMounts:
- name: config
mountPath: /app/config.json
subPath: config.json
livenessProbe:
httpGet:
path: /v2/health
port: 3536
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /v2/health
port: 3536
initialDelaySeconds: 5
periodSeconds: 10
volumes:
- name: config
configMap:
name: signal-fish-config
---
apiVersion: v1
kind: Service
metadata:
name: signal-fish-service
spec:
selector:
app: signal-fish
ports:
- protocol: TCP
port: 80
targetPort: 3536
type: LoadBalancer
Monitoring¶
Health Checks¶
Returns 200 OK when healthy.
Metrics¶
JSON metrics:
Prometheus metrics:
Prometheus Configuration¶
YAML
scrape_configs:
- job_name: 'signal-fish'
scrape_interval: 15s
static_configs:
- targets: ['signal-fish:3536']
metrics_path: /metrics/prom
Scaling Considerations¶
Signal Fish Server uses in-memory storage, so each instance maintains its own room state. For multi-instance deployments:
- Session affinity - Use sticky sessions at the load balancer
- Room sharding - Route by game_name or room_code
- Health checks - Monitor each instance independently
- Graceful shutdown - Allow in-flight connections to complete
Resource Requirements¶
Typical resource usage per instance:
- CPU: 0.25-0.5 cores (idle), 1-2 cores (active)
- Memory: 128-512 MB (depends on room count)
- Network: Low bandwidth (WebSocket messages are small)
Scale based on:
- Active rooms per instance (recommend < 500)
- Active players per instance (recommend < 2000)
- Messages per second (recommend < 10000)
Logging¶
Set log level:
Levels: trace, debug, info, warn, error
Enable file logging:
JSON
{
"logging": {
"enable_file_logging": true,
"dir": "logs",
"filename": "server.log",
"rotation": "daily",
"format": "Json"
}
}
Security Checklist¶
- Enable authentication (
require_websocket_auth: true) - Set strong app secrets
- Configure CORS origins (not
*) - Use HTTPS/TLS in production
- Set rate limits appropriately
- Limit max_connections_per_ip
- Enable metrics authentication
- Use a reverse proxy (nginx/Caddy)
- Monitor failed auth attempts
- Rotate secrets regularly
Next Steps¶
- Configuration - Full configuration reference
- Authentication - Securing your server