Geliştirme sürecinde en sinir bozucu şeylerden biri, her küçük kod değişikliğinden sonra docker compose down && docker compose up döngüsünü tekrarlamak. Bir satır değiştirdin, konteyneri durduracaksın, yeniden build edeceksin, tekrar ayağa kaldıracaksın. Bu süreç hem zaman kaybettiriyor hem de geliştirici akışını tamamen kesiyor. Docker Compose’un watch komutu tam olarak bu sorunu çözmek için tasarlandı ve bir kez kullandıktan sonra bir daha eski yönteme dönemiyorsunuz.
watch Komutu Nedir ve Neden Önemlidir
Docker Compose 2.22 sürümüyle birlikte stabil hale gelen watch özelliği, kaynak dosyalarınızı izleyerek değişiklikleri otomatik olarak konteynere yansıtıyor. Geliştirme ortamınızda hot reload, live sync ve otomatik rebuild gibi senaryoları Docker Compose konfigürasyonunuzun içine gömerek yönetmenizi sağlıyor.
Temel mantık şu: Kaynak dizinlerinizi ya da belirli dosyalarınızı izlemeye alıyorsunuz. Bir değişiklik tespit edildiğinde, tanımladığınız aksiyona göre ya dosyayı doğrudan konteynere kopyalıyor, ya konteyneri yeniden başlatıyor, ya da servisi baştan build edip ayağa kaldırıyor. Bu üç aksiyon birbirinden farklı senaryolara hizmet ediyor ve doğru kombinasyonu seçmek iş akışınızı ciddi ölçüde hızlandırıyor.
Bu özelliği gerçekten değerli kılan şey, her şeyin compose.yaml dosyasının içinde tanımlanması. Takımdaki herkesin aynı geliştirme deneyimini yaşaması için ekstra araç kurulumu veya konfigürasyon dosyası paylaşımına gerek kalmıyor.
Gereksinimler ve Kurulum
watch komutunu kullanabilmek için birkaç temel gereksinim var.
- Docker Engine 23.0 veya üzeri gerekiyor, daha eski sürümlerde bu özellik desteklenmiyor
- Docker Compose v2.22.0 veya üzeri olması gerekiyor, v1 tamamen devre dışı
- Compose dosyasının
develop.watchbloğunu destekleyen formatta yazılmış olması gerekiyor
Mevcut versiyonunuzu kontrol etmek için:
docker --version
docker compose version
Eğer eski bir sürümünüz varsa güncelleme yapmanız gerekiyor. Ubuntu/Debian sistemlerde:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Temel Konfigürasyon Yapısı
watch özelliği compose.yaml dosyasının içindeki develop bloğu altında tanımlanıyor. Önce en basit haliyle başlayalım:
services:
web:
build: .
ports:
- "3000:3000"
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
Bu konfigürasyonda iki kural var. ./src dizinindeki herhangi bir değişiklik doğrudan konteynerin /app/src dizinine kopyalanıyor (rebuild olmadan). package.json dosyasında bir değişiklik olursa servis baştan build edilip yeniden başlatılıyor.
Komutu çalıştırmak için:
docker compose watch
Ya da arka planda normal up ile başlatıp watch’ı ayrı bir terminalde çalıştırabilirsiniz:
docker compose up -d
docker compose watch
Üç Aksiyon Tipi: Detaylı İnceleme
sync
sync aksiyonu, değişen dosyaları doğrudan çalışan konteynere kopyalar. Konteyner yeniden başlatılmaz, build alınmaz. Bu aksiyonu genellikle uygulama kodunun kendi kendini yenileyen çerçevelerle (Node.js, Python Flask debug modu, Go Air gibi araçlarla) kullanıyorsunuz.
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
- "*.test.js"
- .env.local
ignore listesi önemli bir detay. node_modules gibi büyük dizinleri izleme listesinden çıkarmak hem performansı artırıyor hem de gereksiz tetiklemeleri önlüyor.
sync+restart
Bu aksiyon, dosyayı konteynere kopyaladıktan sonra konteyneri yeniden başlatıyor. Rebuild almadan ama yeniden başlatma gerektiren konfigürasyon dosyaları, ortam değişkenleri veya statik dosyalar için ideal.
develop:
watch:
- action: sync+restart
path: ./config
target: /app/config
- action: sync+restart
path: ./nginx.conf
target: /etc/nginx/nginx.conf
rebuild
rebuild aksiyonu tam anlamıyla yeniden build alıp konteyneri ayağa kaldırıyor. Dependency dosyaları (package.json, requirements.txt, go.mod gibi), Dockerfile değişiklikleri veya temel konfigürasyon değişiklikleri için kullanıyorsunuz. Bu aksiyon diğerlerine göre daha yavaş ama bazı durumlarda kaçınılmaz.
develop:
watch:
- action: rebuild
path: package.json
- action: rebuild
path: Dockerfile
- action: rebuild
path: requirements.txt
Gerçek Dünya Senaryosu 1: Node.js Express API
Bir Express.js API’sini geliştirdiğinizi düşünün. Nodemon gibi bir araçla birlikte watch kullanmak muhteşem bir geliştirme deneyimi sunuyor.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
package.json içinde dev script’i:
{
"scripts": {
"dev": "nodemon --watch src src/index.js"
}
}
compose.yaml:
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
- action: rebuild
path: package.json
- action: rebuild
path: package-lock.json
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Bu konfigürasyonla ./src altındaki herhangi bir .js dosyasını değiştirdiğinizde, Docker watch dosyayı konteynere kopyalıyor, nodemon değişikliği algılayıp Node.js sürecini yeniden başlatıyor. package.json değiştiğinde ise tam rebuild alınıyor.
docker compose watch
Çıktı şöyle görünüyor:
Watch enabled
- Watching build context for service "api"
- Syncing "api" after changes were detected
Gerçek Dünya Senaryosu 2: Python FastAPI Uygulaması
FastAPI ile geliştirme yaparken uvicorn‘un --reload seçeneği hayat kurtarıyor. Watch ile birleşince çok güçlü bir ortam elde ediyorsunuz.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
services:
fastapi:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
develop:
watch:
- action: sync
path: ./app
target: /app/app
ignore:
- __pycache__/
- "*.pyc"
- .pytest_cache/
- action: sync+restart
path: ./alembic
target: /app/alembic
- action: rebuild
path: requirements.txt
- action: rebuild
path: pyproject.toml
redis:
image: redis:7-alpine
ports:
- "6379:6379"
worker:
build: .
command: celery -A app.worker worker --loglevel=info
depends_on:
- redis
develop:
watch:
- action: sync+restart
path: ./app
target: /app/app
ignore:
- __pycache__/
- "*.pyc"
Burada dikkat çeken nokta, worker servisinin de kendi watch bloğu olması. Her servisin bağımsız olarak izleme kuralları tanımlanabiliyor.
Gerçek Dünya Senaryosu 3: Full-Stack React + Go Projesi
Hem frontend hem backend barındıran projelerde watch özelliği gerçek gücünü gösteriyor.
services:
frontend:
build:
context: ./frontend
target: development
ports:
- "5173:5173"
develop:
watch:
- action: sync
path: ./frontend/src
target: /app/src
ignore:
- node_modules/
- dist/
- action: sync
path: ./frontend/public
target: /app/public
- action: rebuild
path: ./frontend/package.json
- action: rebuild
path: ./frontend/vite.config.ts
backend:
build:
context: ./backend
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://dev:devpassword@db:5432/myapp
develop:
watch:
- action: sync+restart
path: ./backend
target: /app
ignore:
- vendor/
- "*.test.go"
- action: rebuild
path: ./backend/go.mod
- action: rebuild
path: ./backend/go.sum
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpassword
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Go tarafında Air gibi bir hot reload aracı kullanıyorsanız sync aksiyonu yeterli. Eğer yoksa sync+restart ile her değişiklikte binary’yi yeniden derlemeniz gerekiyor, bu biraz daha yavaş ama çalışıyor.
İzleme Kurallarını Hassas Ayarlama
Büyük projelerde neyin izleneceğini dikkatli seçmek performans açısından kritik. Birkaç önemli nokta var.
Glob pattern kullanımı: ignore listesinde glob pattern destekleniyor.
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- "**/*.test.ts"
- "**/*.spec.ts"
- "**/node_modules/**"
- "**/.git/**"
- "**/coverage/**"
Birden fazla path izleme: Aynı aksiyon için birden fazla kural tanımlayabilirsiniz.
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: sync
path: ./templates
target: /app/templates
- action: sync
path: ./static
target: /app/static
- action: rebuild
path: Dockerfile
- action: rebuild
path: .dockerignore
watch ile Birlikte Kullanışlı Bayraklar
docker compose watch komutunun birkaç kullanışlı seçeneği var.
–no-up: Servisleri başlatmadan sadece watch modunu etkinleştiriyor. Servisler zaten çalışıyorsa bunu kullanıyorsunuz.
docker compose watch --no-up
Belirli servisleri izlemek: Tüm servisleri değil, sadece belirli servisleri izlemek isteyebilirsiniz.
docker compose watch frontend backend
Logları birlikte görmek: Watch çalışırken logları da takip etmek için ayrı terminal açmanız gerekiyor.
# Terminal 1
docker compose watch
# Terminal 2
docker compose logs -f
Sık Karşılaşılan Sorunlar ve Çözümleri
Sorun: Değişiklikler konteynere yansımıyor
İlk kontrol edilecek şey .dockerignore dosyası. Eğer sync etmek istediğiniz dosya ya da dizin .dockerignore içinde listeleniyorsa watch bu dosyaları görmezden geliyor.
cat .dockerignore
sync için hedef path’in konteynerdeki doğru konumu gösterdiğinden emin olun. İçeride /app/src diye bir dizin yoksa sync başarısız olabilir.
Sorun: rebuild çok sık tetikleniyor
IDE’ler bazen dosyaları kaydetmeden önce geçici dosyalar oluşturuyor. Bunları ignore listesine ekleyin.
ignore:
- "*.swp"
- "*.swo"
- ".idea/"
- ".vscode/"
- "*~"
Sorun: macOS’ta watch çok yavaş çalışıyor
macOS’ta Docker Desktop, host dosya sistemini sanal makineye bağlarken ek bir gecikme yaratıyor. Docker Desktop ayarlarından “VirtioFS” dosya sistemi seçeneğini aktif etmek bu sorunu büyük ölçüde çözüyor. Ayrıca Docker Desktop 4.18 ve sonrasında gelen synchronized file shares özelliği de bu konuda ciddi iyileştirme sağlıyor.
Sorun: watch komutu hata veriyor
Compose dosyasının develop.watch bloğunu tanıması için en az Compose v2.22 gerekiyor. Eski compose syntax’ında bu blok yok.
docker compose version
# Docker Compose version v2.24.0 gibi bir şey görmelisiniz
.dockerignore ile Uyumlu Çalışma
Watch özelliği kullanırken .dockerignore dosyanızı dikkatli yazmanız gerekiyor. Build context için gereksiz olan şeyleri exclude etmek isterken, watch’ın sync etmesi gereken dosyaları exclude etmemelisiniz.
# .dockerignore
node_modules
dist
build
.git
.env
.env.local
coverage
*.log
.DS_Store
Burada src dizini exclude edilmiyor çünkü hem build sırasında hem de watch sync sırasında bu dizine ihtiyaç var.
CI/CD Entegrasyonu ve Ortam Ayrımı
watch özelliği yalnızca geliştirme ortamı için tasarlanmış, bunu production’da kullanmak istemezsiniz. Bunu yönetmek için birkaç yaklaşım var.
Birincisi, ayrı compose dosyaları kullanmak:
# Geliştirme için
docker compose -f compose.yaml -f compose.dev.yaml watch
# Production için
docker compose -f compose.yaml -f compose.prod.yaml up -d
compose.dev.yaml içinde watch bloklarını tanımlayıp ana compose.yaml‘ı temiz tutabilirsiniz.
# compose.dev.yaml
services:
web:
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
İkincisi, profiles kullanmak:
services:
web:
build: .
ports:
- "3000:3000"
profiles: ["development"]
develop:
watch:
- action: sync
path: ./src
target: /app/src
docker compose --profile development watch
Performans İpuçları
Büyük monorepo yapılarında ya da çok sayıda dosya içeren projelerde watch’ı verimli kullanmak için birkaç ipucu:
- Mümkün olan en dar path’i verin: Tüm proje kökü yerine sadece değişen alt dizini izleyin
- ignore listesini agresif kullanın: Test dosyaları, coverage raporları, build çıktıları kesinlikle ignore’a eklensin
- rebuild aksiyonunu minimize edin: Sadece gerçekten build gerektiren dosyalar için rebuild tanımlayın, geri kalanı sync veya sync+restart ile halledin
- Birden fazla servis izlerken dikkatli olun: Her servis için ayrı watch thread çalışıyor, çok fazla servis ve çok geniş path kombinasyonu sistem kaynaklarını tüketebilir
Sonuç
Docker Compose watch komutu, konteyner tabanlı geliştirme iş akışında gerçek bir paradigma değişikliği sunuyor. Artık her küçük değişiklik için manuel container yönetimi yapmak zorunda kalmıyorsunuz. sync, sync+restart ve rebuild aksiyonlarını doğru kombinasyonda kullanarak hem hız hem de güvenilirlik elde ediyorsunuz.
Özellikle ekiple çalışan projelerde bu konfigürasyonun compose.yaml içine gömülmesi büyük avantaj sağlıyor. Yeni bir geliştirici projeyi clone’layıp docker compose watch yazdığında otomatik olarak aynı geliştirme deneyimini yaşıyor. Ekstra araç kurulumu, özel script veya konfigürasyon dosyası paylaşımı gerekmiyor.
Başlangıç için Node.js veya Python projelerinizde temel bir sync + rebuild kombinasyonuyla başlayıp zamanla konfigürasyonu projenizin ihtiyaçlarına göre şekillendirin. Watch özelliğini bir kez günlük iş akışınıza kattığınızda, onsuz nasıl geliştirme yaptığınızı hatırlamak zorlaşıyor.