Redis cache
Keep firewall verdicts across restarts and share them between instances. Recommended before metadata filtering.
The firewall caches every verdict it gets from Socket. By default that cache lives in the container's memory, so it is lost on every restart and isn't shared between instances. Adding Redis fixes both. Set it up before Metadata filtering: filtering works without it, but Redis keeps its per-version verdicts across restarts and shares them between instances.
The cache holds verdicts only (allow, warn, block), not package files. Package bytes always stream from the upstream registry.
Before you start: a running firewall (Block installs with Firewall).
How the two cache windows work
| Window | Setting | Default | Behavior |
|---|---|---|---|
| Fresh | cache.ttl | 600 s | Served from cache immediately |
| Stale | redis.ttl | 86400 s | Still in Redis. An expired block is served as-is; an expired allow is re-checked with Socket before it's served (default cache.confirm_allow_mode: wait) |
| Expired | after redis.ttl | Removed from Redis; the next request asks Socket |
1. Add Redis to the compose file
Add a redis service next to the firewall in docker-compose.yml. Redis is only reachable on the compose network, not from the host.
services:
socket-firewall:
# ...existing settings...
depends_on:
- redis
redis:
image: redis:7-alpine
container_name: socket-firewall-redis
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--appendonly", "yes"]
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:--appendonly yes and the volume keep the cache on disk, so restarting Redis doesn't empty it.
2. Point the firewall at Redis
Append to .env. Compose also reads REDIS_PASSWORD from here for the redis service.
REDIS_ENABLED=true
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=change_me_to_a_long_random_stringGenerate the password with openssl rand -hex 32.
Use environment variables, notsocket.yml, for the passwordA
redis:block insocket.ymlworks too, but values in that file are not shell-expanded, sopassword: ${REDIS_PASSWORD}is stored as that literal string. Environment variables keep the password out of a file that usually lives in version control.
3. Recreate the stack
docker compose up -d --force-recreate
docker compose ps # both containers Up; firewall (healthy) after about 45 sVerify
Generate a verdict, then confirm it landed in Redis and survives a firewall restart.
# Helper: redis-cli with the password from .env
rc() { docker compose exec -T redis redis-cli --no-auth-warning -a "$(grep '^REDIS_PASSWORD=' .env | cut -d= -f2-)" "$@"; }
rc PING # PONG
rc DBSIZE # note the number
curl -sk -o /dev/null https://sfw.example.com/npm/lodahs/-/lodahs-0.0.1-security.tgz
rc DBSIZE # higher than before
docker compose restart socket-firewall
sleep 30
rc DBSIZE # unchanged: the cache survived the restart
curl -sk -o /dev/null https://sfw.example.com/npm/lodahs/-/lodahs-0.0.1-security.tgz
docker compose logs socket-firewall | grep SOCKET_DECISION | tail -1-
PINGreturnsPONG -
DBSIZEgoes up after a package request -
DBSIZEis unchanged afterdocker compose restart socket-firewall - The decision logged after the restart shows
"cached":true
Running more than one instance
Point every firewall instance at the same Redis (REDIS_HOST = a shared Redis or a managed service such as ElastiCache or Azure Cache for Redis). Instances then share verdicts, so a package checked by one is served from cache by all.
For a managed Redis with TLS, add to .env:
REDIS_SSL=true
REDIS_SSL_VERIFY=true
# Only if needed:
REDIS_SSL_CA_CERT=/etc/redis-tls/ca.pem # private CA
REDIS_SSL_CLIENT_CERT=/etc/redis-tls/client.pem # mTLS
REDIS_SSL_CLIENT_KEY=/etc/redis-tls/client-key.pem
REDIS_SSL_SERVER_NAME=redis.example.com # SNI, if it differs from REDIS_HOSTMount the certificate files into the container (for example ./redis-tls:/etc/redis-tls:ro under volumes:). The same settings exist under redis: in socket.yml (ssl_ca_cert, ssl_client_cert, ssl_client_key, ssl_server_name).
Clearing the cache
Restarting the firewall no longer clears verdictsOther pages suggest restarting the firewall to pick up a policy change or waiver immediately. With Redis, verdicts survive the restart. To force fresh verdicts, flush Redis instead.
rc FLUSHDB # rc helper from Verify aboveFlushing is safe: the next request for each package asks Socket again.
Troubleshooting
| Symptom | Fix |
|---|---|
DBSIZE stays at 0 | REDIS_ENABLED isn't true, the firewall wasn't recreated after editing .env, or the firewall can't reach Redis (next row) |
| Firewall is healthy but Redis is unused | The firewall falls back to its in-memory cache and stays (healthy). Check docker compose logs socket-firewall | grep -i "redis" for Failed to connect to Redis |
NOAUTH Authentication required from redis-cli | Pass the password with -a |
Failed to connect to Redis: connection refused [role=primary localhost:6379] | REDIS_HOST must be the compose service name (redis), not localhost |
| Cache empty after a Redis restart | Persistence is off. Check the --appendonly yes flag and the redis-data volume |
Next
Reference
Updated 8 days ago