Skip to content
DevOps devops databases 5 min read

Running Databases in Docker

Installing a database the traditional way (with apt) puts it permanently on your machine, ties you to one version, and can be a pain to remove. Docker (a tool that runs software inside lightweight, isolated boxes called containers) lets you start a fresh Postgres, MySQL, MongoDB, or Redis in seconds, run several versions side by side, and throw them away when you are done. This page shows you the exact commands to run each database in Docker, how to keep your data safe with volumes, and how to wire it all together with Docker Compose.

Why run a database in a container

A container is a running copy of a packaged image (a read-only template that bundles the database software and everything it needs). Because the container is isolated, the database does not touch your host operating system. You get a clean, repeatable setup that behaves the same on every machine.

When to use this: local development, automated tests, trying a new database version, or running throwaway environments. It is the fastest way to get a database for a project without polluting your laptop.

When NOT to use this: a serious production database that holds real customer data. Containers add a layer of complexity around storage, backups, and networking that you must get exactly right, and a single bad docker rm can wipe everything. For production, prefer a managed service (like Amazon RDS) or a carefully configured dedicated server. See the caution below.

First, install Docker on Ubuntu 22.04/24.04 LTS if you have not already:

sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker $USER   # log out and back in for this to take effect

Check it works:

docker --version

Output:

Docker version 27.5.1, build 27.5.1-0ubuntu3~24.04.1

The big gotcha: data lives in a volume

This is the single most important idea on this page. By default, anything a container writes is stored inside the container. When you run docker rm to delete that container, all the data goes with it. People lose their database constantly this way.

The fix is a named volume (a storage area that Docker manages on your host and keeps alive independently of any container). You attach the volume to the folder where the database keeps its files. The container can come and go; the volume — and your data — stays.

Warning: If you run a database container without a volume, your data is deleted the moment the container is removed. Always map a named volume to the database’s data directory. There is no undo.

Running PostgreSQL

PostgreSQL stores its data in /var/lib/postgresql/data inside the container. We map a named volume there so the data survives.

docker run -d \
  --name pg \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_USER=appuser \
  -e POSTGRES_DB=appdb \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  postgres:17

What each flag means:

FlagMeaning
-dRun detached (in the background)
--name pgGive the container a friendly name
-e KEY=VALUESet an environment variable the image reads on startup
-p 5432:5432Map host port : container port so apps on your laptop can connect
-v pgdata:/var/lib/...Attach the named volume pgdata for persistence

Connect to it from inside the container to confirm:

docker exec -it pg psql -U appuser -d appdb -c "SELECT version();"

Output:

                                  version
----------------------------------------------------------------------------
 PostgreSQL 17.4 (Debian 17.4-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled...
(1 row)

Running MySQL

docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=rootsecret \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=secret \
  -p 3306:3306 \
  -v mysqldata:/var/lib/mysql \
  mysql:8.4

MySQL keeps its data in /var/lib/mysql. Test the login:

docker exec -it mysql mysql -u appuser -psecret -e "SELECT 1;"

Output:

+---+
| 1 |
+---+
| 1 |
+---+

Running MongoDB

docker run -d \
  --name mongo \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  -p 27017:27017 \
  -v mongodata:/data/db \
  mongo:8

MongoDB stores data in /data/db. Verify with its shell:

docker exec -it mongo mongosh -u admin -p secret --eval "db.runCommand({ ping: 1 })"

Output:

{ ok: 1 }

Running Redis

Redis is an in-memory store, but it can save snapshots to disk. Turn on persistence with --appendonly yes and give it a volume.

docker run -d \
  --name redis \
  -p 6379:6379 \
  -v redisdata:/data \
  redis:7 redis-server --appendonly yes --requirepass secret

Output (after docker exec -it redis redis-cli -a secret PING):

PONG

Doing it all with Docker Compose

Typing long docker run commands gets old fast. Docker Compose lets you describe your containers in one docker-compose.yml file and start everything with a single command. Create the file:

services:
  postgres:
    image: postgres:17
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7
    command: redis-server --appendonly yes --requirepass secret
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data
    restart: unless-stopped

volumes:
  pgdata:
  redisdata:

Start and stop the whole stack:

docker compose up -d      # start everything in the background
docker compose ps         # see what is running
docker compose down       # stop and remove containers (volumes are KEPT)

Output of docker compose ps:

NAME                IMAGE         STATUS         PORTS
project-postgres-1  postgres:17   Up 8 seconds   0.0.0.0:5432->5432/tcp
project-redis-1     redis:7       Up 8 seconds   0.0.0.0:6379->6379/tcp

Note that docker compose down keeps your named volumes. To delete the data too, run docker compose down -v — only do this when you truly want a clean slate.

Inspecting and removing volumes

docker volume ls            # list all volumes
docker volume inspect pgdata
docker volume rm pgdata     # permanently delete (container must be gone first)

Tip: On Ubuntu, named volumes live under /var/lib/docker/volumes/. Do not edit those files by hand — let the database manage them. To move data between machines, use the database’s own backup tools (like pg_dump or mongodump), not a raw file copy.

Best practices

  • Always attach a named volume to the data directory so a docker rm never destroys your data.
  • Pin a specific image tag (postgres:17, not postgres:latest) so rebuilds are reproducible and a surprise major upgrade does not break you.
  • Never hardcode real passwords in committed Compose files — use a .env file or Docker secrets, and add .env to .gitignore.
  • Only publish the port (-p) when you actually need host access; on a shared server bind to localhost with -p 127.0.0.1:5432:5432 so the database is not exposed to the internet.
  • Use restart: unless-stopped so dev databases come back after a reboot.
  • Take regular backups with the database’s native dump tools, and test that you can restore them.
  • Treat container databases as disposable for dev; for production, prefer a managed database or a hardened dedicated install.
Last updated June 15, 2026
Was this helpful?