快適なオンボーディング エクスペリエンス (「pnpm dev:infra をクローンして実行する」) と苦痛なオンボーディング エクスペリエンス (「最初に PostgreSQL をセットアップし、次に Redis を構成し、その後...」) の違いは、Docker Compose セットアップがインフラストラクチャ要件をどの程度適切に把握しているかによって決まります。うまく作成された docker-compose.dev.yml を使用すると、開発者はどのマシンでも、まったく同じインフラストラクチャを数分で実行できるようになります。
快適なオンボーディング エクスペリエンス (「pnpm dev:infra をクローンして実行する」) と苦痛なオンボーディング エクスペリエンス (「最初に PostgreSQL をセットアップし、次に Redis を構成し、その後...」) の違いは、Docker Compose セットアップがインフラストラクチャ要件をどの程度適切に把握しているかによって決まります。うまく作成された docker-compose.dev.yml を使用すると、開発者はどのマシンでも、まったく同じインフラストラクチャを数分で実行できるようになります。
このガイドでは、サービス構成、ヘルスチェック、ネットワーキング、ボリューム管理、アプリケーションの起動シーケンスとの統合など、運用品質のローカル開発スタックのパターンについて説明します。
重要なポイント
- システム インストールとの競合を避けるために、PostgreSQL のデフォルト以外のポート (5433) をローカルで使用します。
- サービスの依存関係のヘルスチェックにより、「接続が拒否されました」という起動エラーを防止します
- 名前付きボリュームはコンテナーの再起動間でデータベース データを保持します — Windows ではバインド マウントが確実に機能しません
env_fileを使用して、環境変数を.env.localファイルからコンテナにロードしますdocker-compose.dev.ymlをdocker-compose.prod.ymlから分離します - それぞれ異なる目的を果たしますdepends_on.condition: service_healthyパターンは、コンテナーの開始だけでなく、実際の準備が完了するまで待機しますprofilesを使用してオプション サービス (電子メール、モニタリング) をオプトインにしますdocker-compose(v1) ではなくdocker compose(v2) を実行します — プラグイン構文は最新のものです
完全な開発スタック
# infrastructure/docker-compose.dev.yml
name: ecosire-dev
services:
# ─── PostgreSQL ─────────────────────────────────────────────────
postgres:
image: postgres:17-alpine
container_name: ecosire-postgres
environment:
POSTGRES_DB: ecosire_dev
POSTGRES_USER: ecosire
POSTGRES_PASSWORD: dev_password_change_in_prod
ports:
- "5433:5432" # 5433 externally — avoids conflicts with system PostgreSQL
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d # Run SQL on first start
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=1GB
-c work_mem=16MB
-c maintenance_work_mem=128MB
-c checkpoint_completion_target=0.9
-c wal_buffers=16MB
-c max_connections=100
-c log_min_duration_statement=100
-c log_statement=ddl
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ecosire -d ecosire_dev"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# ─── Redis ──────────────────────────────────────────────────────
redis:
image: redis:7-alpine
container_name: ecosire-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: >
redis-server
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--appendonly yes
--appendfsync everysec
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# ─── Authentik (Identity Provider) ──────────────────────────────
authentik-server:
image: ghcr.io/goauthentik/server:2024.12
container_name: ecosire-authentik
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ecosire
AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
volumes:
- authentik_media:/media
- authentik_certs:/certs
ports:
- "9000:9000" # HTTP
- "9443:9443" # HTTPS
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "ak healthcheck"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s # Authentik takes time to initialize
restart: unless-stopped
authentik-worker:
image: ghcr.io/goauthentik/server:2024.12
container_name: ecosire-authentik-worker
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ecosire
AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
volumes:
- authentik_media:/media
- authentik_certs:/certs
- /var/run/docker.sock:/var/run/docker.sock # For Authentik's proxy
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
# ─── Mailpit (Email Testing) ─────────────────────────────────────
mailpit:
image: axllent/mailpit:latest
container_name: ecosire-mailpit
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
environment:
MP_MAX_MESSAGES: 200
MP_SMTP_AUTH_ACCEPT_ANY: true
MP_SMTP_AUTH_ALLOW_INSECURE: true
restart: unless-stopped
profiles:
- email # Optional — use `docker compose --profile email up`
# ─── pgAdmin (Database GUI) ─────────────────────────────────────
pgadmin:
image: dpage/pgadmin4:latest
container_name: ecosire-pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: admin@local.dev
PGADMIN_DEFAULT_PASSWORD: admin
PGADMIN_CONFIG_SERVER_MODE: "False"
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
ports:
- "5050:80"
volumes:
- pgadmin_data:/var/lib/pgadmin
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
profiles:
- tools # Optional
networks:
default:
name: ecosire-dev-network
volumes:
postgres_data:
name: ecosire-postgres-data
redis_data:
name: ecosire-redis-data
authentik_media:
name: ecosire-authentik-media
authentik_certs:
name: ecosire-authentik-certs
pgadmin_data:
name: ecosire-pgadmin-data
Package.json スクリプト
Docker Compose コマンドを monorepo スクリプトに接続します。
{
"scripts": {
"dev:infra": "docker compose -f infrastructure/docker-compose.dev.yml up -d",
"dev:infra:down": "docker compose -f infrastructure/docker-compose.dev.yml down",
"dev:infra:logs": "docker compose -f infrastructure/docker-compose.dev.yml logs -f",
"dev:infra:reset": "docker compose -f infrastructure/docker-compose.dev.yml down -v && pnpm dev:infra",
"dev:infra:email": "docker compose -f infrastructure/docker-compose.dev.yml --profile email up -d",
"dev:infra:tools": "docker compose -f infrastructure/docker-compose.dev.yml --profile tools up -d"
}
}
--profile フラグを使用すると、オプションのサービス (Mailpit による電子メール テスト、pgAdmin によるデータベース GUI) は、明示的に要求されるまで休止状態のままになります。
データベース初期化スクリプト
SQL ファイルを infrastructure/init-scripts/ に配置します。これらは最初のコンテナーの起動時に実行されます。
-- infrastructure/init-scripts/01-create-databases.sql
-- Create all databases Authentik needs separately from the app DB
CREATE DATABASE authentik;
GRANT ALL PRIVILEGES ON DATABASE authentik TO ecosire;
-- Create test database for CI
CREATE DATABASE ecosire_test;
GRANT ALL PRIVILEGES ON DATABASE ecosire_test TO ecosire;
-- infrastructure/init-scripts/02-extensions.sql
-- Enable PostgreSQL extensions
\c ecosire_dev;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- Trigram search
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- Composite GIN indexes
\c ecosire_test;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
初期化スクリプトはアルファベット順に実行されます。 \c database_name psql メタコマンドは、アクティブなデータベースを切り替えます。
環境変数の統合
アプリケーションはモノリポジトリのルートにある .env.local から読み取ります。 Docker サービスは、サービス名 (localhost ではない) を使用して相互に接続する方法を知っている必要があります。
# .env.local (monorepo root)
# PostgreSQL — use 5433 externally (host) or 5432 internally (container network)
DATABASE_URL=postgresql://ecosire:dev_password_change_in_prod@localhost:5433/ecosire_dev
# Redis
REDIS_URL=redis://localhost:6379
# Authentik — use 9000 for external calls from your dev machine
AUTHENTIK_URL=http://localhost:9000
# Use service name for server-to-server calls within Docker network
AUTHENTIK_INTERNAL_URL=http://authentik-server:9000
# Email (Mailpit SMTP)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false
# Application
NODE_ENV=development
Docker 内で実行され、他のサービスと通信する必要があるアプリケーションの場合は、サービス名を使用します。ホスト マシン (開発モードの NestJS、Next.js) で実行されているアプリケーションの場合は、ホスト マップされたポートで localhost を使用します。
ヘルスチェックの詳細
ヘルスチェックにより、連鎖的な起動エラーが防止されます。 depends_on.condition: service_healthy は、コンテナーの開始だけでなく、実際の準備が完了するまで待機します。
# Without health checks — can fail because PostgreSQL isn't ready
depends_on:
- postgres
# With health checks — waits for PostgreSQL to accept connections
depends_on:
postgres:
condition: service_healthy
独自のサービスのカスタム ヘルス チェック:
// apps/api/src/health/health.controller.ts
@Get()
@Public()
@HealthCheck()
async check() {
return this.health.check([
() => this.db.isHealthy('database'),
() => this.redis.isHealthy('redis'),
]);
}
# If your API is also dockerized
api:
image: ecosire-api:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
ボリューム管理
名前付きボリュームは、再起動間でデータを保持します。各ボリューム タイプをいつ使用するかを理解します。
| タイプ | 永続性 | パフォーマンス | 用途 |
|---|---|---|---|
| 名前付きボリューム | はい | 素晴らしい | データベースデータ |
| バインドマウント | はい | 良い (Linux)、悪い (macOS) | ソースコードのホットリロード |
| tmpfs | いいえ | 素晴らしい | 一時ファイル、シークレット |
# Use bind mounts for source code (enables hot reload)
volumes:
- ./apps/api/src:/app/src # Code changes reflected immediately
# Use named volumes for data
volumes:
- postgres_data:/var/lib/postgresql/data
# Use tmpfs for ephemeral data
volumes:
- type: tmpfs
target: /tmp
Docker Desktop を備えた macOS では、バインド マウントは gRPC FUSE を使用しますが、これは Linux よりも大幅に遅くなります。 NestJS および Next.js 開発サーバーの場合は、(Docker ではなく) ホスト マシン上で直接実行して、ネイティブ ファイル システムのパフォーマンスを取得します。
プロダクション Docker Compose
本番構成ファイルは構造的に異なります。ローカル ポート、再起動ポリシー、本番リソース制限はありません。
# infrastructure/docker-compose.prod.yml
name: ecosire-prod
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
# No port mapping — only accessible within Docker network
restart: always
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 512M
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 1gb
volumes:
- redis_data:/data
restart: always
# No port mapping — internal only
volumes:
postgres_data:
redis_data:
運用環境ではポートが外部に公開されません。アプリケーションは内部 Docker ネットワーク経由で接続します。 Nginx は外部トラフィックを処理します。
よくある落とし穴と解決策
落とし穴 1: ポートがシステム サービスと競合する
PostgreSQL、Redis、およびその他のサービスは、多くの場合、システム サービスとして実行されます。開発中は常に非標準ポートにマップします。
- PostgreSQL:
5433:5432(5432:5432ではありません) - Redis:
6379:6379を保持します (競合はほとんどありません) lsof -i :5432を実行してデフォルトのポートを使用しているものを確認します
落とし穴 2: Linux でのボリューム権限の問題
Linux 上の Docker ボリュームは、デフォルトで root 所有権を使用します。コンテナ ユーザーが root 以外の場合は、正しい所有権を設定します。
postgres:
image: postgres:17-alpine
user: "999:999" # postgres user's UID:GID
# Or use init container to fix permissions
落とし穴 3: Authentik の初期化には 60 秒以上かかります
Authentik は初回起動時にデータベース移行を実行します。ヘルスチェックの start_period: 60s により時間が与えられます。 Authentik の準備が整う前に依存サービスが開始されると、サービスは失敗します。 service_healthy 条件を使用し、十分な start_period を与えます。
落とし穴 4: Mac における Docker デスクトップのリソース制限
デフォルトの Docker Desktop は 2 つの CPU と 2GB RAM を割り当てますが、PostgreSQL + Redis + Authentik を同時に実行するには十分ではありません。 [Docker デスクトップ設定] > [リソース] を少なくとも 4 つの CPU と 6GB RAM に増やします。
落とし穴 5: docker-compose と docker compose
古い docker-compose (v1、Python で記述) は非推奨になりました。 docker compose (v2、プラグイン) を使用します。バージョンを確認してください: docker compose version。 Docker Compose version v2.x.x が表示される場合は、v2 を使用しています。
よくある質問
開発中にアプリケーション サービス (NestJS、Next.js) を Docker で実行する必要がありますか?
通常はいいえ。アクティブな開発の場合は、ホットリロードを高速化し、デバッグを容易にするために、ホスト マシン上でアプリケーション サービスを実行します。 Docker は、安定していて頻繁に再起動する必要のないインフラストラクチャ サービス (データベース、キャッシュ、アイデンティティ プロバイダー) にのみ使用してください。例外は、アプリケーションに開発 OS と運用環境の間で異なるネイティブ依存関係がある場合です。
Docker Compose ワークフローでデータベース移行を処理するにはどうすればよいですか?
インフラストラクチャ pnpm dev:infra && pnpm db:migrate を開始した後、ホスト マシンから移行を実行します。開発中に Docker コンテナ内で移行を実行しないでください。Drizzle 移行を安全にする型チェックと IDE 統合が失われます。最初のデータベース作成には、Docker の initdb.d スクリプトを使用します。
ローカルの Docker ボリュームをバックアップおよび復元するにはどうすればよいですか?
docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data を使用してバックアップします。 tar xzf を使用して同じアプローチで復元します。開発の場合は、ポートが公開されているため、pg_dump でダンプし、psql で復元することもできます。
Docker Compose の状態を他のチーム メンバーと共有するにはどうすればよいですか?
Docker Compose ファイルは git 経由で共有されますが、ボリューム内のデータはローカルにあります。各開発者は空のデータベースから開始し、移行/シードを実行してデータベースにデータを追加します。シード スクリプト (リポジトリにコミットされている) を使用して、一貫したテスト データを作成します。共有 docker-compose.dev.yml により、全員が同じサービス バージョンと構成を使用できるようになります。
開発時に実際の電子メールの代わりに Mailpit を使用する理由は何ですか?
Mailpit は、すべての送信電子メールをキャプチャし、それらを表示するための Web UI を提供するローカル SMTP サーバーです。開発中に実際のユーザーに実際の電子メールを誤って送信することを防ぎ、SMTP 資格情報を必要とせず、受信トレイをチェックせずに電子メール テンプレートを検証できます。 SMTP_HOST=localhost SMTP_PORT=1025 を使用するようにアプリを構成し、http://localhost:8025 にアクセスしてキャプチャされたメールを表示します。
次のステップ
ローカル開発用によく練られた Docker Compose セットアップは、新しい開発者が参加したり、新しいマシンを起動したりするたびに、必ず利益をもたらす投資です。 ECOSIRE は、チーム全体のローカル開発のために PostgreSQL 17、Redis 7、および Authentik を Docker Compose で実行します。
ローカル開発インフラストラクチャの設計や、実稼働用のアプリケーションのコンテナ化についてサポートが必要ですか? 当社の DevOps サービスをご覧ください して、当社がどのようにお手伝いできるかをご確認ください。
執筆者
ECOSIRE Research and Development Teamテクニカルライティング
ECOSIRE のテクニカルライティングチームは、Odoo ERP、Shopify による EC、AI エージェント、Power BI による分析、GoHighLevel による自動化、そしてエンタープライズソフトウェアのベストプラクティスを扱っています。私たちのガイドは、企業が十分な情報に基づいてテクノロジーを判断できるよう支援します。
関連記事
Odoo Python 開発: 初心者とプロのための完全ガイド
モジュール構造、ORM API、ビュー、コントローラー、継承パターン、デバッグ、テストを網羅したこの完全なガイドで、Odoo Python 開発をマスターしましょう。
ウェブアプリケーションのための AWS EC2 デプロイメントガイド
完全な AWS EC2 デプロイ ガイド: インスタンスの選択、セキュリティ グループ、Node.js デプロイ、Nginx リバース プロキシ、SSL、自動スケーリング、CloudWatch モニタリング、コストの最適化。
2026 年のクラウド ERP とオンプレミス ERP: 決定版ガイド
2026 年のクラウド ERP とオンプレミス ERP: 総コスト分析、セキュリティ比較、拡張性、コンプライアンス、ビジネスに適した導入モデル。