A language-agnostic, ready-to-run local database stack for developers. Bring up MySQL, PostgreSQL, Redis, Mailpit, plus web UIs (phpMyAdmin for MySQL and Adminer for Postgres) with one command. Works for PHP, Node.js, Python, and more.
This repository provides a simple, consistent way to run common data services for local and dev environments. It is designed to help teams and individual developers quickly spin up MySQL, Postgres, Redis, and an SMTP sink (Mailpit) along with web UIs for database administration.
- Purpose: one-command local DB stack for any language.
- Audience: backend/web developers, QA, CI preview environments.
- Benefits: fast onboarding, reproducible environments, zero native DB installs.
- Scope: local and development use only; not hardened for production.
Repository layout highlights:
- Compose file: docker-compose.yml
- Env sample: example.env
- MySQL config: my.cnf
- MySQL logs: logs/
- Persistent data: sail-mysql/, sail-postgres/, sail-redis/
- CI workflows: .github/workflows/compose-validate.yml, .github/workflows/release.yml
| Service | Host Port | Internal | UI Link |
|---|---|---|---|
| MySQL | 3306 | 3306 | phpMyAdmin → http://localhost:8083 |
| PostgreSQL | 5432 | 5432 | Adminer → http://localhost:8084 |
| Redis | 6379 | 6379 | — |
| Mailpit (SMTP) | 1025 | 1025 | Mailpit → http://localhost:8025 |
| Mailpit (UI) | 8025 | 8025 | Mailpit → http://localhost:8025 |
| phpMyAdmin | 8083 | 80 | http://localhost:8083 |
| Adminer | 8084 | 8080 | http://localhost:8084 |
- MySQL 8.0 (persistent storage, custom
my.cnf, logs) - PostgreSQL 13 (persistent storage)
- Redis (persistent storage)
- Mailpit (SMTP testing + web inbox)
- phpMyAdmin (MySQL UI)
- Adminer (Postgres UI)
- A shared Docker network (
sail) so apps can connect by service name
Optional (commented out in docker-compose.yml):
- MinIO (S3-compatible object storage) + bucket bootstrap
- A sample Laravel Sail app service
- Install Docker Desktop (macOS/Windows/Linux).
- Copy env sample (if present) and set credentials:
cp example.env .env
# Edit .env to set DB_USERNAME, DB_PASSWORD, etc.- Start the stack:
docker compose up -d- Stop when done:
docker compose down- MySQL:
3306(overridable viaFORWARD_DB_PORT) - PostgreSQL:
5432 - Redis:
6379(overridable viaFORWARD_REDIS_PORT) - Mailpit SMTP:
1025(overridable viaFORWARD_MAILPIT_PORT) - Mailpit UI:
8025(overridable viaFORWARD_MAILPIT_DASHBOARD_PORT) - phpMyAdmin UI:
8083 - Adminer UI:
8084
Set these in .env (Compose automatically loads it):
DB_USERNAME: database user (used for MySQL and Postgres)DB_PASSWORD: database password (used for MySQL and Postgres)FORWARD_DB_PORT: host port for MySQL (default 3306)FORWARD_REDIS_PORT: host port for Redis (default 6379)FORWARD_MAILPIT_PORT: host port for SMTP (default 1025)FORWARD_MAILPIT_DASHBOARD_PORT: host port for Mailpit UI (default 8025)- Optional MinIO:
MINIO_ROOT_USER,MINIO_ROOT_PASSWORD(if you uncomment MinIO)
- Hostnames: inside Docker use
mysql; from host uselocalhost. - Credentials:
root+DB_PASSWORD, orDB_USERNAME/DB_PASSWORD. - Volumes:
./sail-mysql:/var/lib/mysql(data persistence)./my.cnf:/etc/mysql/my.cnf(config)./logs:/var/log/mysql(logs)
- Healthcheck:
mysqladmin ping -p${DB_PASSWORD}.
- Hostnames: inside Docker use
postgres; from host uselocalhost. - Credentials:
POSTGRES_USER=DB_USERNAME,POSTGRES_PASSWORD=DB_PASSWORD. - Volume:
./sail-postgres:/var/lib/postgresql/data.
- Hostnames: inside Docker use
redis; from host uselocalhost. - Volume:
./sail-redis:/data.
- SMTP server on
:1025; web UI on:8025. - Use it as an SMTP sink for dev/testing (captures outbound mails).
- UI for MySQL at
http://localhost:8083. - Configured to talk to the
mysqlservice.
- UI for Postgres at
http://localhost:8084. - Default server is the
postgresservice.
Use the service names on the Docker network (sail) when your app runs in containers, or localhost with mapped ports when running on your host.
- App inside the same Compose network: use service names (
mysql,postgres,redis,mailpit). No ports needed inside the network unless you changed defaults. - App on your host machine: use
localhostand the mapped ports (e.g.,3306,5432,6379). - App in a different container not attached to
sail:- Option A (recommended): attach that container to the
sailnetwork and use service names. - Option B: use the host-mapped ports with
host.docker.internal(macOS/Windows) as the host, e.g.,host.docker.internal:3306for MySQL. On Linux, use the Docker gateway IP (often172.17.0.1).
- Option A (recommended): attach that container to the
Already running container:
docker network connect sail <your_container_name>
# Then inside that container, use hosts like: mysql, postgres, redis, mailpitStart a new container on the network:
docker run --rm -it --network sail alpine:3.20 sh
# apk add --no-cache mysql-client
# mysql -hmysql -u$DB_USERNAME -p$DB_PASSWORD -P3306Compose service in another repo/project (declare external network):
networks:
sail:
external: true
services:
myapp:
image: node:20-alpine
networks:
- sailDB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=your_db_name
DB_USERNAME=${DB_USERNAME}
DB_PASSWORD=${DB_PASSWORD}
REDIS_HOST=redis
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="dev@example.com"
MAIL_FROM_NAME="Local Dev"MySQL (mysql2):
const mysql = require('mysql2/promise');
const conn = await mysql.createConnection({
host: 'localhost', // or 'mysql' if inside Docker
port: 3306,
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
});Postgres (pg):
const { Client } = require('pg');
const client = new Client({
host: 'localhost', // or 'postgres' if inside Docker
port: 5432,
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
});
await client.connect();Redis (ioredis):
const Redis = require('ioredis');
const redis = new Redis({ host: 'localhost', port: 6379 });MySQL (mysqlclient or PyMySQL):
import pymysql
conn = pymysql.connect(host='localhost', port=3306,
user='YOUR_USER', password='YOUR_PASS')Postgres (psycopg):
import psycopg
conn = psycopg.connect(host='localhost', port=5432,
user='YOUR_USER', password='YOUR_PASS')Redis (redis-py):
import redis
r = redis.Redis(host='localhost', port=6379)- phpMyAdmin (MySQL): http://localhost:8083
- Adminer (Postgres): http://localhost:8084
- Mailpit: http://localhost:8025
- MySQL data:
./sail-mysql/ - Postgres data:
./sail-postgres/ - Redis data:
./sail-redis/ - MySQL config:
./my.cnf - MySQL logs:
./logs/
Back up or remove these folders to reset data.
Uncomment in docker-compose.yml:
- MinIO stack (
minio,createbuckets) and setMINIO_ROOT_USER,MINIO_ROOT_PASSWORD. - The sample
laravel.testapp service if you want an app container.
# Check container status
docker compose ps
# Tail logs for a service
docker compose logs -f mysql
# MySQL CLI (container)
docker compose exec mysql mysql -uroot -p${DB_PASSWORD}
# Redis ping
docker compose exec redis redis-cli pingThis repository includes a lightweight GitHub Actions workflow that validates the Compose file on pushes and pull requests.
- Workflow: .github/workflows/compose-validate.yml
- It creates a minimal
.envand runsdocker compose config -qto catch syntax or interpolation issues early.
- Port in use: change forwarded ports in
.env(e.g.,FORWARD_DB_PORT). - Credentials fail: ensure
.envis loaded and values are set; recreate containers if needed (downthenup -d). - Reset data: stop the stack and remove
sail-mysql,sail-postgres, andsail-redisfolders (this deletes all data).
This stack is intended for local and dev use only. Do not expose these services directly to the public internet without hardening.
- Open issues and PRs for improvements, docs, or new services.
- Follow the existing style for Compose services and environment variables.
- For significant changes, discuss in an issue first.
This project is licensed under the MIT License. See LICENSE for details.