איך לפרוס אפליקציות עם Docker Compose על VPS
מדריך ייצור מעשי ל-Docker Compose v2 על Ubuntu: התקינו את המנוע, כתבו קובץ Compose עם רשתות וכרכים, טפלו בסודות, שימו פרוקסי הפוך מלפנים, עדכנו בגלילות בלי פאניקה וגבו כרכים בעלי שם.

Docker Compose הוא הדרך הרגילה להריץ מחסנית קטנה על VPS אחד: אפליקציית ווב, מסד נתונים, מטמון Redis ופרוקסי הפוך, כולם בקובץ YAML אחד. זה לא Kubernetes. לא תקבלו כשל-גיבוי רב-צומתי. תקבלו התקנה שחוזרת על עצמה וניתנת לסקירה שאפשר ליצור מחדש על VPS חדש של Hiddence בדקות — בדיוק מה שרוב פרויקטי צד ומוצרים קטנים צריכים.
המאמר מניח שאתם רוצים משהו קרוב יותר לייצור מ-docker run בליל שישי. נתקין Docker Engine ואת תוסף Compose, ניצור משתמש פריסה שאינו root, נכתוב קובץ Compose עם בדיקות בריאות ומדיניות הפעלה מחדש, נשמור סודות מחוץ ל-Git, נפרסם לאינטרנט רק את פורטי הפרוקסי ההפוך, ונגדיר שגרת עדכון וגיבוי. המחסנית לדוגמה היא אפליקציית ווב טיפוסית פלוס PostgreSQL פלוס Caddy, אבל אותו דפוס עובד ל-Node, PHP, Python או חבורת עובדים.
למה Compose על VPS בודד עדיין הגיוני
אנשים קופצים ל-Kubernetes כי זה אופנתי ואז מבלים את סוף השבוע ב-YAML לאתר אחד. Compose נשאר קריא. מגרסאים את הקובץ, מתעדים משתני סביבה, ואפשר להשוות שינוי לפני שמחילים אותו. הבידוד מספיק טוב: קונטיינר אפליקציה שנפרץ לא אמור להחזיק את שקע UNIX של המסד אם השתמשתם ברשתות ובמשתמשים עם הרשאה מינימלית. מגבלות משאבים עוצרות דליפת זיכרון אחת מלהקפיא את כל ה-VPS.
- קובץ אחד מתאר את כל המחסנית
- כרכים בעלי שם שורדים יצירה מחדש של קונטיינרים
- רשתות Docker פנימיות משאירות את Postgres מחוץ לאינטרנט הציבורי
- restart: unless-stopped מכסה רוב האתחולים מחדש
- קל להעתיק ל-VPS שני כשגדלים מהראשון
דרישות
השתמשו ב-VPS עם מספיק RAM לאפליקציה פלוס Postgres. תוכנית 2 GB היא מינימום ריאלי לאפליקציה + מסד + פרוקסי. מניחים Ubuntu 24.04. אל תתקינו Docker מ-Snap אקראי בלי לקרוא מה הוא עושה ל-cgroups; הצעדים למטה משתמשים במאגר apt הרשמי של Docker.
- Ubuntu 22.04/24.04, root או sudo
- מומלץ 2 GB RAM (1 GB רק למחסניות זעירות בלי Postgres)
- דומיין שמצביע ל-VPS אם רוצים HTTPS אוטומטי
- Git או דרך אחרת להעתיק את הפרויקט לשרת
שלב 1: התקינו Docker Engine ו-Compose v2
Compose v2 הוא תוסף שמופעל כ-docker compose (עם רווח), לא הבינארי הישן docker-compose בפייתון. התקינו את שניהם ממאגר Docker כדי לקבל תיקוני אבטחה עדכניים.
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שלב 2: משתמש פריסה ופריסת תיקיות
להריץ Compose כ-root עובד, אבל משתמש ייעודי עם חברות בקבוצת docker נקי יותר. שימו את הפרויקט ב-/opt או /srv, לא ב-/root, כדי שגיבויים והרשאות יהיו ברורים. לעולם אל תהפכו את התיקייה שמכילה .env לניתנת לכתיבה לכל העולם.
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שלב 3: כתבו קובץ Compose עם חשיבה ייצורית
הקובץ למטה הוא תבנית. החליפו את תמונת האפליקציה בשלכם. Postgres לא מפורסם ל-0.0.0.0:5432 — רק Caddy מפורסם על 80 ו-443. בדיקות בריאות עוצרות פרוקסי הפוך מלשלוח תעבורה לאפליקציה שעדיין מעבירה את המסד. נעצו תגי תמונה; latest הוא איך קורה שבירה מפתיעה.
# /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:שלב 4: סודות, .env ו-Caddyfile
שימו סיסמאות ב-.env בשרת, chmod 600, ולעולם אל תבצעו commit לקובץ הזה. צרו סיסמאות עם openssl rand -base64 32. ה-Caddyfile צריך רק לעשות פרוקסי הפוך לשם שירות האפליקציה ברשת Docker.
# /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שלב 5: חומת אש, יומנים ומגבלות משאבים
UFW צריך לאפשר רק 22, 80 ו-443. Docker לפעמים עוקף UFW לפורטים מפורסמים; אם צריך חומת אש מחמירה במארח, בדקו את האינטראקציה הנוכחית של Docker + UFW לגרסת Ubuntu שלכם או פרסמו פורטים רק על 127.0.0.1 ושימו Caddy מארח מלפנים. הגדירו מגבלות זיכרון ב-Compose כדי ש-Postgres לא יאכל את ה-VPS.
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שלב 6: עדכונים וחזרה לאחור
העדכון המשעמם שעובד: משכו תמונות חדשות, up -d, צפו בבדיקות בריאות, שמרו את התג הקודם ב-git כדי לנעוץ בחזרה. אל תעשו docker compose down על מסד ייצור אלא אם מתכוונים להפסיק לקבל תעבורה. down -v מוחק כרכים — זה מחיקה, לא הפעלה מחדש.
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גבו כרכים בעלי שם
תמונת מצב של VPS טובה. dump לוגי של Postgres טוב יותר אם צריך לשחזר על מכונה אחרת. תזמנו זאת עם cron. בדקו שחזור פעם אחת, אחרת אין לכם גיבויים — יש קבצים שאתם מקווים שהם גיבויים.
# 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פתרון בעיות
קראו docker compose ps ו-docker compose logs service. כשלי משיכת תמונה הם בדרך כלל מגבלות קצב של ghcr/docker hub או תמונה פרטית בלי התחברות. 502 מ-Caddy אומר שהאפליקציה לא בריאה או ששם המארח של הפרוקסי שגוי (השתמשו בשם שירות Compose, לא localhost, מתוך קונטיינר Caddy). שגיאות הרשאה על כרכים לעיתים קרובות אומרות שהתמונה רצה כ-uid 1000 בעוד תיקיית המארח היא root.
- compose: command not found — התקנתם את שם הבינארי הישן; השתמשו ב-docker compose
- port is already allocated — משהו אחר מחזיק 80/443 (Apache, Nginx, Caddy אחר)
- database connection refused — האפליקציה התחילה לפני ש-Postgres היה מוכן; השתמשו ב-healthcheck + תנאי depends_on
- disk full — docker system df ואז גזמו תמונות לא בשימוש בזהירות
- permission denied on docker.sock — המשתמש לא בקבוצת docker, או צריך הפעלת כניסה חדשה
אבטחה
אל תפרסמו פורטי מסד. אל תריצו privileged: true. אל תעגנו /var/run/docker.sock לקונטיינר אפליקציה אלא אם כותבים במכוון מנהל קונטיינרים. שמרו על המנוע מעודכן. סרקו תמונות משלכם אם בונים אותן. .env בדיסק עדיין קובץ סוד — הגבילו מי יכול לעשות ssh לקופסה.
- בלי 5432 / 3306 / 6379 ציבוריים
- נעצו תקצירי תמונה או לפחות תגים בלתי משתנים
- chmod 600 .env
- Unattended-upgrades למערכת המארח עדיין חשובים
- פרויקט Compose אחד לכל אפליקציה שומר על רדיוס פיצוץ קטן יותר
טיפים
- שימו compose.yaml ב-git; שמרו .env.example בלי סודות אמיתיים
- השתמשו בפרופילים לעובדים אופציונליים (docker compose --profile workers up -d)
- צפו ב-docker stats כשמתאימים גודל VPS
- עגינת קוד מקור אינה דפוס הייצור — אפו תמונה
- אם המחסנית גדלה לכמה VPS, אז תסתכלו על תזמור — לא לפני
התקנת Compose כמעט-ייצורית על VPS אחד היא: חבילות Docker רשמיות, .env נעול, קובץ YAML שלא מפרסם את המסד, בדיקות בריאות, פרוקסי הפוך על 80/443, UFW, תגי תמונה שאפשר לחזור אליהם, ו-dump-ים של Postgres ששיחזרתם באמת פעם. התחילו מהתבנית במאמר, החליפו את תמונת האפליקציה, והתייחסו ל-docker compose down -v כפקודה הרסנית באותו כבוד כמו rm -rf.