Bloga dön
Ağustos 19, 2026Rehberler

VPS Üzerinde Docker Compose ile Uygulamalar Nasıl Dağıtılır

Ubuntu’da Docker Compose v2 için pratik bir üretim rehberi: motoru kurun, ağlar ve volume’larla bir Compose dosyası yazın, sırları yönetin, öne bir ters proxy koyun, paniksiz güncellemeler yapın ve adlandırılmış volume’ları yedekleyin.

VPS Üzerinde Docker Compose ile Uygulamalar Nasıl Dağıtılır

Docker Compose, tek bir VPS’te küçük bir yığını çalıştırmanın olağan yoludur: bir web uygulaması, bir veritabanı, bir Redis önbelleği ve bir ters proxy, hepsi bir YAML dosyasında. Kubernetes değildir. Çok düğümlü yedekleme almazsınız. Yeni bir Hiddence VPS’te dakikalar içinde yeniden oluşturabileceğiniz tekrarlanabilir, gözden geçirilebilir bir kurulum alırsınız — çoğu yan proje ve küçük ürünün tam olarak ihtiyacı budur.

Bu makale, Cuma gecesi docker run’dan üretime daha yakın bir şey istediğinizi varsayar. Docker Engine ve Compose eklentisini kuracağız, root olmayan bir dağıtım kullanıcısı oluşturacağız, sağlık kontrolleri ve yeniden başlatma politikaları olan bir Compose dosyası yazacağız, sırları Git’ten uzak tutacağız, internete yalnızca ters proxy portlarını yayınlayacağız ve bir güncelleme ile yedekleme rutini tanımlayacağız. Örnek yığın tipik bir web uygulaması artı PostgreSQL artı Caddy’dir, ancak aynı desen Node, PHP, Python veya bir grup worker için de çalışır.

Tek bir VPS’te Compose neden hâlâ mantıklı

İnsanlar moda olduğu için Kubernetes’e atlar, sonra bir web sitesi için hafta sonunu YAML ile geçirir. Compose okunaklı kalır. Dosyayı sürümlersiniz, ortam değişkenlerini belgelersiniz ve uygulamadan önce bir değişikliği diff’leyebilirsiniz. Yalıtım yeterince iyidir: ağlar ve en az ayrıcalıklı kullanıcılar kullandıysanız ele geçirilmiş bir uygulama konteyneri veritabanı UNIX soketini elinde tutmamalıdır. Kaynak limitleri bir bellek sızıntısının tüm VPS’i dondurmasını durdurur.

  • Tek dosya tüm yığını tanımlar
  • Adlandırılmış volume’lar konteyner yeniden oluşturmayı atlatır
  • İç Docker ağları Postgres’i genel internetten uzak tutar
  • restart: unless-stopped çoğu yeniden başlatmayı karşılar
  • İlkini aştığınızda ikinci bir VPS’e kopyalamak kolaydır

Gereksinimler

Uygulama artı Postgres için yeterli RAM’i olan bir VPS kullanın. Uygulama + veritabanı + proxy için gerçekçi minimum 2 GB plandır. Ubuntu 24.04 varsayılır. Cgroups’a ne yaptığını okumadan rastgele bir Snap’ten Docker kurmayın; aşağıdaki adımlar Docker’ın resmi apt deposunu kullanır.

  • Ubuntu 22.04/24.04, root veya sudo
  • 2 GB RAM önerilir (Postgres’siz çok küçük yığınlar için yalnızca 1 GB)
  • Otomatik HTTPS istiyorsanız VPS’e işaret eden bir alan adı
  • Projeyi sunucuya kopyalamanın bir yolu: Git veya başka

Adım 1: Docker Engine ve Compose v2’yi kurun

Compose v2, eski docker-compose Python ikilisi değil, docker compose (boşluklu) olarak çağrılan bir eklentidir. Güncel güvenlik yamalarını almak için ikisini de Docker’ın deposundan kurun.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

docker version
docker compose version

Adım 2: Dağıtım kullanıcısı ve dizin düzeni

Compose’u root olarak çalıştırmak işe yarar, ancak docker grubunda üyeliği olan ayrılmış bir kullanıcı daha temizdir. Projeyi /root’ta değil /opt veya /srv’ye koyun ki yedekler ve izinler açık olsun. .env içeren dizini asla dünyaya yazılabilir yapmayın.

bash
adduser --disabled-password --gecos '' deploy
usermod -aG docker deploy
mkdir -p /srv/app
chown deploy:deploy /srv/app
chmod 750 /srv/app

# Log in as deploy for the rest of the file editing:
# su - deploy
# cd /srv/app

Adım 3: Üretim odaklı bir Compose dosyası yazın

Aşağıdaki dosya bir şablondur. Uygulama imajını sizinkiyle değiştirin. Postgres 0.0.0.0:5432’ye yayınlanmaz — yalnızca Caddy 80 ve 443’te yayınlanır. Sağlık kontrolleri, veritabanını hâlâ taşıyan bir uygulamaya ters proxy’nin trafik göndermesini durdurur. İmaj etiketlerini sabitleyin; latest, sürpriz kırılmaların nasıl olduğunu gösterir.

bash
# /srv/app/compose.yaml
services:
  caddy:
    image: caddy:2.8-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      app:
        condition: service_healthy

  app:
    image: ghcr.io/example/webapp:1.4.2
    restart: unless-stopped
    env_file: .env
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"]
      interval: 10s
      timeout: 3s
      retries: 10
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: .env
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - backend

networks:
  frontend: {}
  backend: {}

volumes:
  pgdata:
  caddy_data:
  caddy_config:

Adım 4: Sırlar, .env ve Caddyfile

Parolaları sunucudaki .env’ye koyun, chmod 600 yapın ve o dosyayı asla commit etmeyin. Parolaları openssl rand -base64 32 ile üretin. Caddyfile’ın yalnızca Docker ağındaki uygulama hizmet adına ters proxy yapması gerekir.

bash
# /srv/app/.env (permissions 600)
POSTGRES_USER=app
POSTGRES_PASSWORD=change-me-long-random
POSTGRES_DB=app
# plus any APP_SECRET / NEXTAUTH_SECRET your image needs

# /srv/app/Caddyfile
app.example.com {
    encode gzip
    reverse_proxy app:8000
}

chmod 600 /srv/app/.env
cd /srv/app
docker compose up -d
docker compose ps
docker compose logs -f --tail=100

Adım 5: Güvenlik duvarı, günlükler ve kaynak limitleri

UFW yalnızca 22, 80 ve 443’e izin vermelidir. Docker bazen yayınlanan portlar için UFW’yi atlar; sıkı bir ana makine güvenlik duvarı gerekiyorsa Ubuntu sürümünüz için güncel Docker + UFW etkileşimine bakın veya portları yalnızca 127.0.0.1’de yayınlayıp öne bir ana makine Caddy koyun. Postgres’in VPS’i yememesi için Compose’da bellek limitleri koyun.

bash
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Optional in compose.yaml under app or db:
#    deploy:
#      resources:
#        limits:
#          memory: 512M

# Logs (do not let json-file grow forever):
# { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
# in /etc/docker/daemon.json then: systemctl restart docker

Adım 6: Güncellemeler ve geri alma

İşe yarayan sıkıcı güncelleme: yeni imajları çekin, up -d, sağlık kontrollerini izleyin, geri sabitlemek için önceki etiketi git’te tutun. Trafiği durdurmayı kastetmiyorsanız üretim veritabanında docker compose down yapmayın. down -v volume’ları siler — bu bir yeniden başlatma değil, silmedir.

bash
cd /srv/app
git pull   # if the Compose file lives in git

# Change the image tag in compose.yaml, then:
docker compose pull
docker compose up -d
docker compose ps

# Rollback: set the old tag, pull, up -d again
# NEVER: docker compose down -v   # this deletes named volumes

Adlandırılmış volume’ları yedekleyin

Bir VPS anlık görüntüsü iyidir. Başka bir makineye geri yüklemeniz gerekiyorsa mantıksal bir Postgres dökümü daha iyidir. Bunu cron ile zamanlayın. Geri yüklemeyi bir kez deneyin, yoksa yedekleriniz yoktur — yedek olduğunu umduğunuz dosyalarınız vardır.

bash
# Postgres dump while the db container is running:
docker compose exec -T db pg_dump -U app app | gzip > /var/backups/app-$(date +%F).sql.gz

# Copy off-box
# scp /var/backups/app-*.sql.gz backup-host:~

# Restore sketch (maintenance window):
# gunzip -c app-2026-08-19.sql.gz | docker compose exec -T db psql -U app app

Sorun giderme

docker compose ps ve docker compose logs service okuyun. İmaj çekme hataları genellikle ghcr/docker hub hız limitleri veya giriş olmadan özel bir imajdır. Caddy’den 502, uygulamanın sağlıklı olmadığı veya proxy ana bilgisayar adının yanlış olduğu anlamına gelir (Caddy konteynerinin içinden localhost değil Compose hizmet adını kullanın). Volume’larda izin hataları genellikle imajın uid 1000 olarak çalışması, ana makine dizininin ise root olmasıdır.

  • compose: command not found — eski ikili adını kurdunuz; docker compose kullanın
  • port is already allocated — 80/443’e başka bir şey sahip (Apache, Nginx, başka bir Caddy)
  • database connection refused — uygulama Postgres hazır olmadan başladı; healthcheck + depends_on koşulu kullanın
  • disk full — docker system df, ardından kullanılmayan imajları dikkatle budayın
  • permission denied on docker.sock — kullanıcı docker grubunda değil veya yeni bir oturum gerekir

Güvenlik

Veritabanı portlarını yayınlamayın. privileged: true çalıştırmayın. Kasten bir konteyner yöneticisi yazmıyorsanız /var/run/docker.sock’u bir uygulama konteynerine bağlamayın. Motoru güncel tutun. Kendi imajlarınızı derliyorsanız tarayın. Diskteki .env hâlâ bir sır dosyasıdır — kutuya kimlerin ssh yapabileceğini kısıtlayın.

  • Genel 5432 / 3306 / 6379 yok
  • İmaj özetlerini veya en azından değişmez etiketleri sabitleyin
  • chmod 600 .env
  • Ana işletim sistemi için Unattended-upgrades hâlâ önemlidir
  • Uygulama başına bir Compose projesi patlama yarıçapını küçük tutar

İpuçları

  • compose.yaml’i git’e koyun; gerçek sırlar olmadan .env.example tutun
  • İsteğe bağlı worker’lar için profilleri kullanın (docker compose --profile workers up -d)
  • VPS’i boyutlandırırken docker stats izleyin
  • Kaynak kodunun bağlama bağlanması üretim deseni değildir — bir imaj pişirin
  • Yığın birkaç VPS’e büyürse o zaman orkestrasyona bakın — daha önce değil

Tek bir VPS’te üretime yakın bir Compose kurulumu şudur: resmi Docker paketleri, kilitlenmiş bir .env, veritabanını yayınlamayan bir YAML dosyası, sağlık kontrolleri, 80/443’te bir ters proxy, UFW, geri alabileceğiniz imaj etiketleri ve bir kez gerçekten geri yüklediğiniz Postgres dökümleri. Bu makaledeki şablondan başlayın, uygulama imajını değiştirin ve docker compose down -v komutuna rm -rf ile aynı saygıyı gösterin.