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

WindowSettingDefaultBehavior
Freshcache.ttl600 sServed from cache immediately
Staleredis.ttl86400 sStill 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)
Expiredafter redis.ttlRemoved 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_string

Generate the password with openssl rand -hex 32.

📘

Use environment variables, not socket.yml, for the password

A redis: block in socket.yml works too, but values in that file are not shell-expanded, so password: ${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 s

Verify

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
  • PING returns PONG
  • DBSIZE goes up after a package request
  • DBSIZE is unchanged after docker 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_HOST

Mount 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 verdicts

Other 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 above

Flushing is safe: the next request for each package asks Socket again.

Troubleshooting

SymptomFix
DBSIZE stays at 0REDIS_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 unusedThe 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-cliPass 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 restartPersistence is off. Check the --appendonly yes flag and the redis-data volume

Next

Metadata filtering.

Reference


Did this page help you?