Docker Compose for Development: Yerel Altyapı
Keyifli bir başlangıç deneyimi ("pnpm dev:infra kopyala ve çalıştır") ile acı verici bir deneyim ("önce PostgreSQL'i kurun, sonra Redis'i yapılandırın, sonra...") arasındaki fark, Docker Compose kurulumunuzun altyapı gereksinimlerinizi ne kadar iyi yakaladığıyla ilgilidir. İyi hazırlanmış bir docker-compose.dev.yml, herhangi bir makinedeki herhangi bir geliştiricinin dakikalar içinde tam olarak aynı altyapıyı çalıştırmasını sağlar.
Bu kılavuz, üretim kalitesinde bir yerel geliştirme yığınına yönelik kalıpları kapsar: hizmet yapılandırması, durum denetimleri, ağ oluşturma, birim yönetimi ve uygulamanızın başlatma sırası ile entegrasyon.
Önemli Çıkarımlar
- Sistem kurulumlarıyla çakışmaları önlemek için yerel olarak PostgreSQL için varsayılan olmayan bir bağlantı noktası kullanın (5433)
- Hizmet bağımlılıklarındaki durum denetimleri "bağlantı reddedildi" başlatma hatalarını önler
- Adlandırılmış birimler, kapsayıcı yeniden başlatmaları arasında veritabanı verilerini korur; bağlama bağlamaları Windows'ta güvenilir şekilde çalışmaz
- Ortam değişkenlerini
.env.localdosyanızdan kapsayıcılara yüklemek içinenv_filekullanındocker-compose.dev.ymliledocker-compose.prod.yml'yi ayırın — farklı amaçlara hizmet ederlerdepends_on.condition: service_healthymodeli yalnızca konteynerin başlatılmasını değil, gerçek hazırlığı bekler- İsteğe bağlı hizmetleri (e-posta, izleme) dahil etmek için
profileskullanındocker-compose(v1) değildocker compose(v2) komutunu çalıştırın — eklenti sözdizimi günceldir
Eksiksiz Geliştirme Yığını
# 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: [email protected]
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 Komut Dosyaları
Docker Compose komutlarını monorepo komut dosyalarınıza bağlayın:
{
"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 bayrağı, isteğe bağlı hizmetlerin (Mailpit ile e-posta testi, pgAdmin ile veritabanı GUI'si) açıkça talep edilene kadar hareketsiz kalmasına izin verir.
Veritabanı Başlatma Komut Dosyaları
SQL dosyalarını infrastructure/init-scripts/ içine yerleştirin; bunlar ilk kapsayıcı başlangıcında çalışırlar:
-- 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";
Başlatma komut dosyaları alfabetik sırayla çalışır. \c database_name psql metakomutu etkin veritabanını değiştirir.
Ortam Değişkenleri Entegrasyonu
Uygulamanız monorepo kökündeki .env.local'dan okuyor. Docker hizmetlerinin, hizmet adlarını (localhost değil) kullanarak birbirine nasıl bağlanacağını bilmesi gerekir:
# .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'ın içinde çalışan ve diğer hizmetlerle konuşması gereken uygulamalar için hizmet adlarını kullanın. Ana makinenizde çalışan uygulamalar için (geliştirme modunda NestJS, Next.js), ana makine eşlemeli bağlantı noktalarıyla birlikte localhost kullanın.
Sağlık Kontrolleri Derinlemesine İnceleme
Durum denetimleri art arda gelen başlatma hatalarını önler. depends_on.condition: service_healthy yalnızca konteynerin başlatılmasını değil, gerçek hazırlığı da bekler:
# 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
Kendi hizmetleriniz için özel durum kontrolleri:
// 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
Birim Yönetimi
Adlandırılmış birimler, yeniden başlatmalar arasında verileri korur. Her birim türünün ne zaman kullanılacağını anlayın:
| Tür | Kalıcılık | Performans | Kullanım Amaçlı |
|---|---|---|---|
| Adlandırılmış cilt | Evet | Mükemmel | Veritabanı verileri |
| Bağlama montajı | Evet | İyi (Linux), Kötü (macOS) | Kaynak kodu çalışırken yeniden yükleme |
| tmpf'ler | Hayır | Mükemmel | Geçici dosyalar, sırlar |
# 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 Masaüstüne sahip macOS'ta bağlama bağlamaları, Linux'a göre önemli ölçüde daha yavaş olan gRPC FUSE'yi kullanır. NestJS ve Next.js geliştirme sunucuları için, yerel dosya sistemi performansı elde etmek amacıyla bunları doğrudan ana makinenizde (Docker'da değil) çalıştırın.
Üretim Docker Oluşturma
Üretim oluşturma dosyası yapısal olarak farklıdır; yerel bağlantı noktaları, yeniden başlatma politikaları ve üretim kaynağı sınırları yoktur:
# 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:
Üretim, bağlantı noktalarını harici olarak açığa çıkarmaz; uygulamalar dahili Docker ağı aracılığıyla bağlanır. Nginx harici trafiği yönetir.
Yaygın Tuzaklar ve Çözümler
Tuzak 1: Bağlantı noktası sistem hizmetleriyle çakışıyor
PostgreSQL, Redis ve diğer hizmetler genellikle sistem hizmetleri olarak çalışır. Geliştirme aşamasında her zaman standart olmayan bağlantı noktalarını eşleştirin:
- PostgreSQL:
5433:5432(5432:5432değil) - Redis:
6379:6379'ı koruyun (nadiren çakışır) - Varsayılan bağlantı noktasını neyin kullandığını kontrol etmek için
lsof -i :5432komutunu çalıştırın
Tuzak 2: Linux'ta toplu izin sorunları
Linux'taki Docker birimleri varsayılan olarak kök sahipliğini kullanır. Kapsayıcı kullanıcınız root değilse doğru sahipliği ayarlayın:
postgres:
image: postgres:17-alpine
user: "999:999" # postgres user's UID:GID
# Or use init container to fix permissions
Tuzak 3: Authentik'in başlatılması 60+ saniye sürer
Authentik, veritabanı geçişlerini ilk başlangıçta çalıştırır. Durum kontrolünde start_period: 60s zaman verir. Bağımlı hizmetler Authentik hazır olmadan başlarsa başarısız olurlar. service_healthy koşulunu kullanın ve ona yeterli miktarda start_period verin.
4. Tuzak: Mac'te Docker Masaüstü kaynak sınırları
Varsayılan Docker Masaüstü, 2 CPU ve 2 GB RAM ayırır; bu, PostgreSQL + Redis + Authentik'in aynı anda çalışması için yeterli değildir. Docker Masaüstü Ayarları > Kaynaklar'ı en az 4 CPU ve 6 GB RAM'e yükseltin.
Tuzak 5: docker-compose ve docker compose
Eski docker-compose (v1, Python'da yazılmıştır) kullanımdan kaldırılmıştır. docker compose (v2, eklenti) kullanın. Sürümünüzü kontrol edin: docker compose version. Docker Compose version v2.x.x görüyorsanız v2 kullanıyorsunuz demektir.
Sıkça Sorulan Sorular
Uygulama hizmetlerimi (NestJS, Next.js) geliştirme sırasında Docker'da çalıştırmalı mıyım?
Genellikle hayır; aktif geliştirme için, daha hızlı çalışır durumda yeniden yükleme ve daha kolay hata ayıklama için uygulama hizmetlerinizi ana makinenizde çalıştırın. Docker'ı yalnızca kararlı olan ve sık sık yeniden başlatılması gerekmeyen altyapı hizmetleri (veritabanları, önbellekler, kimlik sağlayıcılar) için kullanın. Bunun istisnası, uygulamanızın geliştirme işletim sisteminiz ile üretim ortamı arasında farklılık gösteren yerel bağımlılıklara sahip olmasıdır.
Docker Compose iş akışında veritabanı geçişlerini nasıl yönetirim?
Altyapıyı başlattıktan sonra taşıma işlemlerini ana makinenizden çalıştırın: pnpm dev:infra && pnpm db:migrate. Geliştirme sırasında geçişleri Docker kapsayıcısının içinde çalıştırmayın; Drizzle geçişlerini güvenli kılan tür denetimini ve IDE entegrasyonunu kaybedersiniz. İlk veritabanı oluşturmak için Docker'ın initdb.d komut dosyalarını kullanın.
Yerel Docker birimlerimi nasıl yedeklerim ve geri yüklerim?
Yedeklemek için docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data kullanın. tar xzf kullanarak aynı yaklaşımla geri yükleyin. Geliştirme için, bağlantı noktasını açıkta bıraktığınız için pg_dump ile dökümü yapabilir ve psql ile geri yükleyebilirsiniz.
Docker Compose durumunu diğer ekip üyeleriyle nasıl paylaşırım?
Docker Compose dosyası git aracılığıyla paylaşılır ancak birimlerdeki veriler yereldir. Her geliştirici boş bir veritabanıyla başlar ve onu doldurmak için geçişleri/tohumları çalıştırır. Tutarlı test verileri oluşturmak için tohum komut dosyalarını (repoya bağlı) kullanın. Paylaşılan docker-compose.dev.yml herkesin aynı hizmet sürümlerini ve yapılandırmasını kullanmasını sağlar.
Geliştirme aşamasında neden gerçek e-posta yerine Mailpit kullanılmalı?
Mailpit, giden tüm e-postaları yakalayan ve bunları görüntülemek için bir web kullanıcı arayüzü sağlayan yerel bir SMTP sunucusudur. Geliştirme sırasında gerçek kullanıcılara yanlışlıkla gerçek e-posta gönderilmesini önler, SMTP kimlik bilgilerini gerektirmez ve e-posta şablonlarını gelen kutunuzu kontrol etmeden doğrulamanıza olanak tanır. Uygulamanızı SMTP_HOST=localhost SMTP_PORT=1025 kullanacak şekilde yapılandırın ve yakalanan e-postaları görmek için http://localhost:8025 adresini ziyaret edin.
Sonraki Adımlar
Yerel kalkınmaya yönelik iyi hazırlanmış bir Docker Compose kurulumu, yeni bir geliştiricinin katıldığı veya yeni bir makineyi çalıştırdığınız her seferde kârını ödeyen bir yatırımdır. ECOSIRE, tüm ekip genelinde yerel gelişim için Docker Compose'da PostgreSQL 17, Redis 7 ve Authentik'i çalıştırıyor.
Yerel geliştirme altyapınızı tasarlamak veya uygulamanızı üretim için kapsayıcı hale getirmek için yardıma mı ihtiyacınız var? Nasıl yardımcı olabileceğimizi görmek için DevOps hizmetlerimizi keşfedin.
Yazan
ECOSIRE Research and Development Team
ECOSIRE'da kurumsal düzeyde dijital ürünler geliştiriyor. Odoo entegrasyonları, e-ticaret otomasyonu ve yapay zeka destekli iş çözümleri hakkında içgörüler paylaşıyor.
İlgili Makaleler
AWS EC2 Deployment Guide for Web Applications
Complete AWS EC2 deployment guide: instance selection, security groups, Node.js deployment, Nginx reverse proxy, SSL, auto-scaling, CloudWatch monitoring, and cost optimization.
Cloud vs On-Premise ERP in 2026: The Definitive Guide
Cloud vs on-premise ERP in 2026: total cost analysis, security comparison, scalability, compliance, and the right deployment model for your business.
Building Custom Odoo Modules: Developer Tutorial
Step-by-step tutorial for building custom Odoo 19 modules. Covers module structure, models, views, security, wizards, and best practices for production-ready code.