How to Run Redis with Docker Compose on Ubuntu

Deploy Redis 8 on Ubuntu with Docker Compose using localhost-only access, authentication, AOF persistence, memory limits, backups, and safe upgrades.

I first ran Redis in Docker on a small Ubuntu server to synchronize configuration between services. Starting the container took one command. Deciding what should happen to the port, password, memory, logs, and data took much longer.

This setup is for a small Redis instance used as a cache or lightweight application dependency on a single Linux host. It keeps Redis reachable only from that host, persists data with AOF, limits memory, rotates container logs, and includes a backup and upgrade path.

If the main question is whether Docker itself is too expensive for a small server, read Docker Performance on Linux vs Docker Desktop. Docker Engine on Linux has a very different resource profile from Docker Desktop on macOS or Windows.

Decide how Redis will be reached

The safest network configuration depends on where the application runs:

Application location Redis connection Compose port setting
On the same Ubuntu host 127.0.0.1:6379 Bind 127.0.0.1:6379:6379
In the same Compose project redis:6379 Do not publish a port
On another server Private network or SSH tunnel Do not publish Redis to the public internet

The example below assumes the application runs directly on the same Ubuntu host. Binding the port to 127.0.0.1 prevents external clients from reaching it through the server’s public interface.

This matters even when ufw is enabled. Docker’s firewall documentation warns that published container ports can bypass some host firewall expectations. A loopback or private-network binding is a better first boundary than a public bind plus a firewall rule.

Prerequisites

Use a currently supported Ubuntu release and install Docker Engine from Docker’s official apt repository. The exact repository commands can change, so follow the current Docker Engine installation guide instead of copying an old package source indefinitely.

Verify both Docker Engine and the Compose plugin:

docker --version
docker compose version

Access to the Docker socket is effectively root-level access to the host. Only add trusted administrative users to the docker group.

Create the service directory and password

Create a dedicated directory:

sudo install -d -m 0750 -o "$USER" -g "$USER" /opt/redis
cd /opt/redis
mkdir -p data

Generate a password in a local .env file:

umask 077
printf 'REDIS_PASSWORD=%s\n' "$(openssl rand -base64 32)" > .env
chmod 600 .env

Do not commit .env. This keeps the password out of the Compose file and shell history, but it is not a full secret manager. A user with root or Docker socket access can still inspect the running container. For a multi-user production platform, use the platform’s secret-management system and Redis ACLs.

Create compose.yaml

Create this file under /opt/redis:

services:
  redis:
    image: redis:8-alpine
    restart: unless-stopped
    ports:
      - "127.0.0.1:6379:6379"
    command:
      - redis-server
      - --appendonly
      - "yes"
      - --appendfsync
      - everysec
      - --maxmemory
      - "64mb"
      - --maxmemory-policy
      - allkeys-lru
      - --requirepass
      - "${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}"
    environment:
      REDIS_PASSWORD: "${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}"
    volumes:
      - ./data:/data
    mem_limit: 128m
    healthcheck:
      test:
        - CMD-SHELL
        - 'REDISCLI_AUTH="$$REDIS_PASSWORD" redis-cli ping | grep -q PONG'
      interval: 10s
      timeout: 3s
      retries: 5
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Several choices here are deliberate:

  • redis:8-alpine pins the major Redis version while still receiving compatible patch releases.
  • 127.0.0.1 keeps the published port on the local host.
  • AOF with appendfsync everysec provides a practical durability/performance balance for a small service.
  • maxmemory 64mb bounds Redis data, while mem_limit: 128m bounds the whole container.
  • allkeys-lru is suitable when Redis is a cache and old keys may be evicted.
  • The health check authenticates without putting the password directly in the command.
  • Log rotation prevents a noisy container from filling the server disk indefinitely.

The Redis data limit and container limit are intentionally different. Redis needs memory for client connections, replication buffers, allocator overhead, persistence work, and other processes beyond stored keys. Do not set the container limit equal to maxmemory.

If Redis stores jobs, configuration, or anything that must not be evicted, replace allkeys-lru with noeviction and size the instance for the expected dataset. Redis persistence improves recovery, but it does not turn an eviction cache into a primary database.

Validate and start Redis

Validate the Compose file without printing the interpolated configuration:

docker compose config --quiet

Then start Redis:

docker compose up -d
docker compose ps

The status should become healthy. Test Redis from inside the container without typing the password into the command:

docker compose exec redis sh -lc \
  'REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli ping'

The expected response is:

PONG

Inspect memory and persistence state:

docker compose exec redis sh -lc \
  'REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli INFO memory'

docker compose exec redis sh -lc \
  'REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli INFO persistence'

docker stats --no-stream

The first startup also creates AOF data under /opt/redis/data. Check that the directory is not empty before assuming persistence is working.

Connect an application on the same host

Load the password into the current shell only for the process that needs it:

set -a
. /opt/redis/.env
set +a
REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h 127.0.0.1 ping
unset REDIS_PASSWORD

Application libraries usually accept a URL in this shape:

redis://:PASSWORD@127.0.0.1:6379/0

Keep that URL in the application’s secret configuration, not in source control or client-side code.

If the application runs in the same Compose project, remove the ports block and connect to redis:6379 over the Compose network. That removes the host port entirely.

Remote access without a public Redis port

For occasional administration from another machine, an SSH tunnel is safer than opening port 6379:

ssh -L 6379:127.0.0.1:6379 user@example-server

While the tunnel is open, a local Redis client can connect to 127.0.0.1:6379. For a permanent connection between servers, use a private network, restrict source addresses at the cloud network layer, enable TLS where required, and prefer Redis ACL users with only the permissions each application needs.

Password authentication alone is not a reason to expose Redis directly to the internet.

Back up the data

AOF protects against normal restarts, but files on the same server are not a backup. Export an RDB snapshot and copy it outside the container:

mkdir -p backups

docker compose exec redis sh -lc \
  'REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli --rdb /tmp/redis-backup.rdb'

docker compose cp \
  redis:/tmp/redis-backup.rdb \
  "./backups/redis-$(date +%F).rdb"

docker compose exec redis rm -f /tmp/redis-backup.rdb

Move the resulting backup to storage outside this server and test restoration before relying on it. A backup that has never been restored is only a hopeful file.

Upgrade Redis safely

Because the image pins Redis 8 rather than a patch release, docker compose pull can bring in a newer Redis 8 image. Before upgrading:

  1. Create and copy a backup.
  2. Read the Redis image and server release notes.
  3. Record the currently running image digest.
  4. Pull and recreate the container.
  5. Verify health, logs, persistence, and an application read/write path.

The update commands are:

docker compose pull redis
docker compose up -d
docker compose ps
docker compose logs --tail=100 redis

For a major-version upgrade, review compatibility and persistence format changes first. Do not change the image tag and the application client at the same time unless there is a tested rollback plan.

Routine checks

A small Redis instance does not need a large operations platform, but it should not be invisible. Check these periodically:

docker compose ps
docker compose logs --tail=100 redis
docker stats --no-stream
df -h
du -sh /opt/redis/data

Also watch application-visible signals: connection errors, command latency, evicted keys, rejected writes under noeviction, and failed persistence operations.

Conclusion

Running Redis with Docker Compose is easy. Running it with clear failure boundaries takes a little more work.

For a small Ubuntu host, the useful defaults are simple: keep Redis off the public interface, authenticate clients, persist data, separate Redis memory from the container limit, rotate logs, export backups, and plan upgrades. Docker makes these choices repeatable, but it does not choose them for you.

Further reading

Loading discussion...