Dockerizing Databases

Table of Contents

  1. PostgreSQL
  2. MySQL
  3. MongoDB
  4. Redis
  5. Initializing Databases
  6. Import/Export Data
  7. Persistent Storage

Running databases in Docker is one of the most common real-world use cases—no more installing PostgreSQL, MySQL, MongoDB, or Redis directly on your machine. This section covers running each of the four most common databases, initializing them with startup data, importing/exporting data, and making sure it all survives container restarts.

1. PostgreSQL

# Run PostgreSQL with credentials set via environment variables
docker run -d \
  --name postgres-db \
  -e POSTGRES_USER=admin \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=app_db \
  -p 5432:5432 \
  -v postgres_data:/var/lib/postgresql/data \
  postgres:16-alpine

# Connect using the psql client bundled in the image
docker exec -it postgres-db psql -U admin -d app_db

2. MySQL

# Run MySQL with credentials set via environment variables
docker run -d \
  --name mysql-db \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=app_db \
  -e MYSQL_USER=admin \
  -e MYSQL_PASSWORD=secret \
  -p 3306:3306 \
  -v mysql_data:/var/lib/mysql \
  mysql:8.4

# Connect using the mysql client bundled in the image
docker exec -it mysql-db mysql -u admin -p app_db

3. MongoDB

# Run MongoDB with a root user set via environment variables
docker run -d \
  --name mongo-db \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  -p 27017:27017 \
  -v mongo_data:/data/db \
  mongo:7

# Connect using the mongosh client bundled in the image
docker exec -it mongo-db mongosh -u admin -p secret

4. Redis

# Run Redis (optionally protected with a password via --requirepass)
docker run -d \
  --name redis-cache \
  -p 6379:6379 \
  -v redis_data:/data \
  redis:7-alpine redis-server --requirepass secret

# Connect using the redis-cli client bundled in the image
docker exec -it redis-cache redis-cli -a secret

5. Initializing Databases

The official PostgreSQL, MySQL, and MongoDB images all run any .sql/.js scripts found in a specific directory the first time the container starts with an empty data directory—perfect for seeding schemas and initial data.

-- init.sql
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL
);
INSERT INTO users (name) VALUES ('Mengsreang');
# PostgreSQL: mount init scripts into /docker-entrypoint-initdb.d
docker run -d \
  --name postgres-db \
  -e POSTGRES_PASSWORD=secret \
  -v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql \
  -v postgres_data:/var/lib/postgresql/data \
  postgres:16-alpine
DatabaseInit Directory
PostgreSQL/docker-entrypoint-initdb.d/
MySQL/docker-entrypoint-initdb.d/
MongoDB/docker-entrypoint-initdb.d/

Note: These init scripts only run against an empty data directory/volume. If the volume already has data (from a previous run), the scripts are silently skipped.

6. Import/Export Data

Each database ships its own dump/restore tooling inside the official image, runnable via docker exec.

PostgreSQL:

# Export (dump) the database to a file on the host
docker exec postgres-db pg_dump -U admin app_db > backup.sql

# Import (restore) from a dump file
cat backup.sql | docker exec -i postgres-db psql -U admin -d app_db

MySQL:

# Export
docker exec mysql-db mysqldump -u admin -psecret app_db > backup.sql

# Import
cat backup.sql | docker exec -i mysql-db mysql -u admin -psecret app_db

MongoDB:

# Export (dumps into a BSON archive inside the container, then copy it out)
docker exec mongo-db mongodump -u admin -p secret --archive=/tmp/backup.archive
docker cp mongo-db:/tmp/backup.archive ./backup.archive

# Import
docker cp ./backup.archive mongo-db:/tmp/backup.archive
docker exec mongo-db mongorestore -u admin -p secret --archive=/tmp/backup.archive

Redis:

# Trigger a synchronous snapshot save to disk (dump.rdb inside the volume)
docker exec redis-cache redis-cli -a secret SAVE

# Copy the resulting RDB snapshot out to the host
docker cp redis-cache:/data/dump.rdb ./dump.rdb

7. Persistent Storage

Every command above mounts a Named Volume (recap: Part 8) so data survives removing and recreating the container—this is not optional for a real database.

DatabaseData Directory to Mount
PostgreSQL/var/lib/postgresql/data
MySQL/var/lib/mysql
MongoDB/data/db
Redis/data
# Create named volumes explicitly ahead of time (optional—docker run creates them automatically)
docker volume create postgres_data
docker volume create mysql_data
docker volume create mongo_data
docker volume create redis_data

In a Compose-based project (recap: Part 10), the same pattern looks like this:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Summary Cheat Sheet

TaskCommand
Run PostgreSQLdocker run -d -e POSTGRES_PASSWORD=... postgres:16-alpine
Run MySQLdocker run -d -e MYSQL_ROOT_PASSWORD=... mysql:8.4
Run MongoDBdocker run -d -e MONGO_INITDB_ROOT_PASSWORD=... mongo:7
Run Redisdocker run -d redis:7-alpine
PostgreSQL dump/restorepg_dump / psql
MySQL dump/restoremysqldump / mysql
MongoDB dump/restoremongodump / mongorestore
Redis snapshotredis-cli SAVE (writes dump.rdb)

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!