Canlı Geliştirme: Docker Compose Watch Komutu ile Otomatik Yenileme

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.watch bloğ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.

Yorum yapın