- לאבחן container שקורס באמצעות
docker ps -a(exit code),docker logs <id>,docker inspect <id>, ו-docker exec -it <id> sh— ולתקן את "container exits immediately" גם כשהוא קורה בלי שורת שגיאה ברורה - לבחור registry מתאים (Docker Hub מול ghcr.io), לתייג image בפורמט
ghcr.io/<user>/<app>:<tag>, ולדחוף ולמשוך אותו בין הלפטופ לשרת - לזהות ולעקוף את ה-pull limits של Docker Hub (100 אנונימי / 200 חינם / ללא הגבלה בתשלום לכל 6 שעות), ולהחליט מתי
docker loginאו ghcr.io פותרים את הבעיה - לבנות image multi-arch עם
docker buildxכך שירוץ גם על Mac M-series (arm64) וגם על VPS amd64, ולהימנע מ-"exec format error" שמופיע כשארכיטקטורת ה-image לא תואמת לארכיטקטורת המכונה - לפרוס את האפליקציה ל-VPS אמיתי — ידנית עם SSH +
docker compose up -d, או דרך Coolify (PaaS קוד-פתוח) עם HTTPS אוטומטי של Let's Encrypt — ולתעד את "מסמך הרצה-בכל-מקום" עם ה-image tag, ה-env vars, וה-volume הקריטי
- פרקים קודמים: 1 (Mental Model), 2 (Install + First Container), 3 (Dockerfile), 4 (Volumes + Secrets), 5 (Compose). הפרק הזה הוא אינטגרציה — הוא מניח שאתם יודעים לבנות image, להריץ container, להגדיר volume, ולחבר services ב-compose.yaml.
- מה תצטרכו: אפליקציה שה-AI בנה + Dockerfile + compose.yaml +
.env(כמו בפרק 5); חשבון GitHub פעיל (נדרש ל-ghcr.io); טרמינל פתוח; גישתsshאם יש לכם VPS (אחרת — תרגילי ה-deploy הראשונים יכולים לרוץ על Coolify ב-Docker Desktop). - זמן משוער: 90-120 דקות (ארוך מהרגיל — זה הפרק שבו הכל מתחבר).
פרק 5 סיימת עם: compose.yaml שמריץ את האפליקציה + מסד-נתונים מקומית, עם named volume לפרסיסטנטיות, healthcheck, ו-depends_on: condition: service_healthy. הכל עובד על הלפטופ. החיסרון: אם תרצו להראות למישהו את האפליקציה מחר, הוא צריך להריץ את הפקודות בעצמו.
בפרק 6 (היום): לוקחים את אותו compose.yaml, בונים את ה-image עם multi-arch (כדי שירוץ בכל ארכיטקטורה), דוחפים אותו ל-ghcr.io (registry חינמי ב-GitHub), מאבחנים כל קריסה של container, ומפרסים ל-VPS אמיתי. הפלט: אפליקציה חיה ב-https://your-app.example.com, ולצדה "מסמך הרצה-בכל-מקום" בן עמוד אחד.
הפרק הבא: זה הפרק האחרון ב-docker-basics. ה-sequel הטבעי הוא קורס production שמרחיב ל-VPS hardening, reverse proxy, observability, ו-CI/CD — כי עכשיו, כשהאפליקציה רצה בכל מקום, השאלה היא לא "איך מריצים" אלא "איך מריצים בבטחה בלי שזה יקרוס ב-3 לפנות בוקר".
'Container exits immediately' — הסיבה מס' 1 לקריסה, ולמה Docker זקוק לתהליך ב-foreground
אם אי פעם הרצתם docker run myapp, ראיתם את ה-container עולה לשנייה, ואז קיבלתם את ההודעה "myapp exited with code 0" או "exited with code 1" — אתם לא לבד. זו הבעיה הנפוצה ביותר בקרב אנשים שמתחילים לעבוד עם Docker, והיא קורה ב-90% מהמקרים בגלל אותה סיבה יסודית.
כדי להבין למה זה קורה, צריך להבין איך Docker מחליט מתי container "חי". Docker לא רואה ב-container תהליך שרץ — הוא רואה תהליך אחד ראשי (PID 1) שחייב להישאר ב-foreground. ברגע שה-PID 1 מסיים (בהצלחה עם exit code 0, או בכישלון עם exit code שונה), ה-container נחשב "מת", ו-Docker מנקה אותו. אין daemon שמחזיק את ה-container בחיים; מה שמחזיק אותו בחיים הוא רק תהליך הראשי.
הסיבה השנייה הנפוצה: env var קריטי חסר. הרבה אפליקציות שה-AI בונה (Next.js, FastAPI, Flask) מצפות לראות DATABASE_URL, SECRET_KEY, או PORT בסביבה. אם ה-env var לא הועבר עם -e או דרך --env-file, האפליקציה תיכשל מיד עם הודעת שגיאה שלא תמיד ברורה. הסיבה השלישית: פקודה שגויה ב-CMD/ENTRYPOINT — למשל, CMD npm star במקום CMD npm start. ה-shell יחזיר "command not found", וה-container יצא תוך אלפית שנייה.
הסיבה הרביעית, הקשה — הרשאות
קבצים שצריכים הרשאת root, אבל ה-Dockerfile הגדיר USER appuser בסוף (תזכורת מפרק 3). קובץ entrypoint.sh בלי chmod +x. Volume שהועתק לתוך ה-image עם הרשאות בעייתיות. כל אלה גורמים ל-container לצאת עם exit code לא-אפס (137 = SIGKILL, 126 = "cannot execute", 127 = "command not found", 1 = "general error").
הנקודה החשובה: אין צורך לנחש. docker ps -a יגיד לכם בדיוק מה ה-exit code, docker logs יראה לכם את הפלט של התהליך שכשל, ו-docker inspect יחשוף mounts, env vars, ומצב. הכלים האלה הם ה"סטטוסקופ" שלכם — ובסעיף הבא נלמד אותם אחד-אחד.
ערכת האבחון: logs, ps -a, inspect, exec, ו-override של entrypoint
חמש פקודות, חמש דרגות של עומק אבחון. כל אחת חושפת שכבה אחרת של מה שקורה בתוך container שלא מתנהג כמצופה. הסדר חשוב: תתחילו תמיד מהזול והמהיר, ורק אז תרדו לפקודות שדורשות הרבה פלט.
1. docker ps -a — רואה גם את המתים
docker ps לבד מראה רק containers רצים. הוסיפו -a (all) ותראו גם את אלה שיצאו. תחפשו את עמודת STATUS: Exited (0) 5 minutes ago = סיים בהצלחה, Exited (1) 5 minutes ago = שגיאה כללית, Exited (137) 5 minutes ago = נהרג (OOM killer או docker stop), Exited (126) 5 minutes ago = הרשאת הרצה. ה-exit code הוא הרמז הראשון שלכם.
2. docker logs <id> — רואה גם אחרי מוות
הפקודה הזו מציגה את stdout ו-stderr של ה-container. היא עובדת גם על container שכבר יצא — זה היתרון הגדול שלה על פני docker exec. הוסיפו --tail 100 כדי לראות רק 100 שורות אחרונות, או -f כדי לעקוב בזמן אמת (כמו tail -f). ברוב המקרים, הודעת השגיאה האחרונה היא הסיבה לקריסה. חפשו שורות עם Error, FATAL, panic, או Traceback.
3. docker inspect <id> — JSON ענק עם הכל
פקודה שמחזירה JSON של 200 שורות עם כל מה ש-Docker יודע על ה-container: env vars, mounts, network, restart count, exit code, מי ה-user שהריץ, מה ה-entrypoint. זו הפקודה הכי צפופה, אבל גם הכי שלמה. טיפ מעשי: צירפו | grep -A 2 "Env" כדי לראות רק את ה-env vars, או | jq '.[0].State.Health.Status' כדי לראות רק את סטטוס ה-healthcheck (דורש התקנת jq). בלי jq אפשר להשתמש ב-python3 -m json.tool כדי לעצב את ה-JSON לקריאה.
בתוך ה-JSON תמצאו גם את "RestartCount" — מספר הפעמים ש-Docker ניסה להריץ את ה-container מחדש. אם הוא גבוה (5+, 10+) זה אומר ש-Docker מנסה להריץ אותו שוב ושוב והוא נופל — סימן ברור שמשהו ב-startup שבור.
4. docker exec -it <id> sh — כניסה לתוך container חי
אם ה-container רץ, אתם יכולים להיכנס פנימה כאילו אתם ב-SSH. הדגלים -i (interactive) ו--t (TTY) נותנים לכם shell אמיתי. בתוך ה-container תוכלו להריץ כל פקודה — ls, cat /etc/os-release, env | grep DATABASE, ps aux — כדי לבדוק מה באמת קורה. זה הכלי הכי חזק לאבחון, כי הוא מאפשר לכם לראות את המצב במקום לנחש אותו.
שימו לב: sh עובד ברוב ה-images (Alpine, distroless עלולים לא לכלול shell). אם sh נותן "executable file not found in $PATH", נסו bash או /bin/sh. ב-image של distroless, תצטרכו להשתמש ב-docker debug (פקודה חדשה יותר שמזרימה shell זמני).
5. Override של entrypoint — כשהכל נופל, תיכנסו ידנית
הסוד האחרון: אתם יכולים לעקוף את ה-CMD/ENTRYPOINT ולהחליף אותו ב-shell. נניח שיש לכם container שיוצא מיד. במקום לראות את ההודעה, תריצו אותו ידנית:
docker run --rm -it --entrypoint sh myapp:latest
# עכשיו אתם בתוך container, אבל במקום להריץ את הפקודה שקורסת, תריצו אותה ידנית:
/app/start.sh
# תראו את השגיאה האמיתית בלי ש-Docker יסגור את ה-container
הסוד הזה לבדו פתר לי 80% מהקריסות של חברי צוות שראיתי. הרעיון: במקום לתת ל-Docker להריץ את הפקודה הכושלת, תריצו אותה אתם, בתוך shell, ותראו את הפלט בזמן אמת. ברגע שהשגיאה מתגלה, התיקון ברור.
פקודה אחת שימושית נוספת: docker cp <id>:/path/in/container /local/path — מעתיקה קובץ החוצה מה-container. שימושי כשאתם רוצים לבדוק לוג פנימי או קובץ קונפיגורציה בלי להיכנס ל-shell.
137 = 128 + 9 = SIGKILL. זה לא תמיד תקין. docker stop שולח SIGTERM ואז SIGKILL, וזה באמת 137. אבל OOM killer של Linux שולח SIGKILL ל-container שחרג ממכסת ה-RAM שלו — וזה גם 137. איך מבדילים? dmesg | grep -i "killed process" על ה-host יראה לכם אם ה-kernel הרג את ה-container. הפתרון: הגדילו את --memory 512m או תייעלו את האפליקציה. אם exit code 137 מופיע בלי שלחצתם docker stop, זה כמעט תמיד זיכרון.
Registries — הגשר מהלפטופ לשרת: Docker Hub מול ghcr.io
עד עכשיו, כל ה-image שלכם חי רק על הלפטופ. כדי להריץ אותו על VPS, על מחשב של חבר, או ב-CI, הוא צריך לעבור דרך מקום משותף — registry. registry זה בעצם Git עבור images: שרת מרכזי שמאחסן images לפי שם, תומך בגרסאות (tags), ומאפשר push (העלאה) ו-pull (הורדה).
ה-flow המלא: docker build יוצר image מקומי → docker tag נותן לו שם-מלא שכולל את ה-registry → docker push מעלה ל-registry → על השרת, docker pull מוריד → docker run מפעיל. בלי registry, אין שום דרך פרקטית להעביר image בין שתי מכונות (חוץ מ-docker save + docker load שמעבירים קובץ .tar — מסורבל לכל deploy שני).
Docker Hub — ה-registry הדיפולטי
Docker Hub (docker.io) הוא ה-registry הציבורי ש-Docker מפעילים. כל docker run nginx שאתם מריצים כבר מושך ממנו. הוא חינמי לשימוש ציבורי (public repos ללא הגבלה), אבל עם מגבלות pull אגרסיביות שנדבר עליהן בסעיף הבא.
ghcr.io — registry חינמי של GitHub
GitHub Container Registry (ghcr.io) הוא registry שמגיע בחינם עם כל חשבון GitHub. הוא יושב ליד ה-repository שלכם, משתמש באותו הרשאות (GHCR PAT = Personal Access Token), ונדחף בקלות עם docker push ghcr.io/<user>/<app>:v1. עבור Vibe Coders שכבר על GitHub — זו ברירת המחדל.
| מאפיין | Docker Hub | ghcr.io |
|---|---|---|
ברירת מחדל ב-docker run | כן (אם לא צוין registry) | לא — חייב prefix מלא |
| Pull limits לא-מחובר | 100 / 6 שעות לכל IP | אין (rate limits נדירים) |
| Pull limits חשבון חינמי | 200 / 6 שעות | גבוה מאוד, לרוב ללא הגבלה לשימוש סביר |
| private repos | 1 חינם, יותר = תשלום | ללא הגבלה (עם חשבון GitHub חינמי) |
| שילוב עם GitHub Actions | דרך secrets | native — token אוטומטי ברוב flows |
| מומלץ ל-Vibe Coder | רק ל-public images מוכרים | כן, ברירת המחדל |
ההבדל המעשי שכדאי לזכור: כל image שתדחפו ל-Docker Hub ציבורי יהיה גלוי לכולם באינטרנט, ואם הוא מכיל סודות (אסור, אבל זה קורה) — הם נחשפים. ב-ghcr.io, גם images "פרטיים" הם חינמיים, ותוכלו להגביל גישה ל-repos ספציפיים. זה חשוב במיוחד לאפליקציות של לקוחות.
מלכודת ה-pull limits של Docker Hub (100/200 לכל 6 שעות)
Docker Hub הציג ב-2020 מגבלות pull שמשתנות לפי סוג החשבון. המספרים הרשמיים נכון לאמצע 2026: 100 pulls לכל 6 שעות לכל IP עבור משתמשים אנונימיים, 200 pulls לכל 6 שעות למשתמשים עם חשבון Personal (חינם), וללא הגבלה למנויי Pro/Team/Business. זה אומר שאם אתם ברשת משרדית או ב-CI עם 5 מכונות שמושכות את אותו image — תוך 30 דקות תקבלו toomanyrequests: You have reached your pull rate limit.
המגבלה היא per IP ולא per user. בבית זה כמעט אף פעם לא בעיה. במשרד עם Wi-Fi משותף, בענן עם NAT, או ב-CI שמריץ 3 jobs במקביל — זה כן בעיה. הפתרון הפשוט: docker login עם חשבון Personal (חינם) מכפיל את המכסה ל-200. הפתרון היותר טוב: עברו ל-ghcr.io ותשכחו מהבעיה.
איך יודעים שנתקלתם במגבלה? הודעת השגיאה תהיה:
Error response from daemon: toomanyrequests:
You have reached your pull rate limit. You may increase
the limit by authenticating and upgrading: https://www.docker.com/pricing
אם ראיתם את זה, יש 3 אפשרויות: (1) docker login והמכסה תקפוץ ל-200; (2) חכו 6 שעות (לא פרקטי); (3) העבירו את ה-image ל-ghcr.io (הפתרון הנכון לטווח ארוך). רוב ה-Vibe Coders שעובדים לבד בבית לא נתקלים במגבלה. מי שעובד ממשרד או ב-CI — נתקלים תוך שבוע.
תרחיש קלאסי: בניתם image עם base node:20-slim (Docker Hub), העליתם ל-Docker Hub, ה-VPS מושך אותו. עובד מצוין במשך 3 ימים. ביום ה-4, בלילה, VPS מנסה למשוך את ה-image של ה-base (אולי כחלק מ-rebuild) — ומקבל toomanyrequests. ה-deploy נשבר ב-3 לפנות בוקר. הפתרון: תמיד docker login ב-VPS לפני ה-deploy הראשון, או עברו ל-ghcr.io עבור images קריטיים לפרודקשן.
ghcr.io — registry חינמי שכבר יושב לכם ב-GitHub
ה-flow המלא של דחיקת image ל-ghcr.io: 4 פקודות, 2 דקות עבודה. הצעד הראשון הוא יצירת Personal Access Token (PAT) ב-GitHub. זה לא הסיסמה שלכם — זו סיסמה נפרדת שנוצרת ב-github.com → Settings → Developer settings → Personal access tokens → Tokens (classic) → "Generate new token". תנו לו scope של write:packages ו-read:packages, ותעתיקו את הערך מיד — GitHub לא יציג אותו שוב.
עכשיו, בטרמינל:
# 1. Login (תדביקו את ה-PAT במקום הסיסמה כשיבקשו)
echo $CR_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
# 2. תיוג ה-image (תחליפו את user/app בשם שלכם)
docker tag myapp:latest ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1
# 3. דחיפה
docker push ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1
# 4. אימות (אופציונלי — משוך בעצמכם מהמקום השני)
docker pull ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1
אחרי docker push, ה-image יופיע תוך 10-30 שניות ב-github.com → Your profile → Packages. אם הוא מוגדר כ-private, רק אתם (ומי שהוספתם ל-collaborators) יראו אותו. אם public — הוא יופיע גם לחיפוש גלובלי של GitHub.
פורמט ה-tag הוא קריטי: ghcr.io/<user>/<app>:<tag>. ה-username חייב להיות זהה ל-GitHub username שלכם, אחרת הדחיפה תיכשל עם denied: requested access to the resource is denied. ה-tag יכול להיות כל מחרוזת — v1, 1.0.0, 2026-06-23, latest. בפרודקשן, אל תשתמשו ב-:latest — זה תמיד מצביע על הגרסה האחרונה שנדחפה, ואתם לא רוצים שה-VPS ימשוך גרסה לא צפויה בלי שתדעו.
הפקודה הקצרה: docker pushx
בעולם האמיתי, תכתבו סקריפט publish.sh שעוטף את כל ה-flow:
#!/bin/bash
# publish.sh — בנה, תייג, ודחוף image ל-ghcr.io
set -e
APP=myapp
VERSION=${1:-v1}
docker build -t $APP:$VERSION .
docker tag $APP:$VERSION ghcr.io/$GITHUB_USER/$APP:$VERSION
docker push ghcr.io/$GITHUB_USER/$APP:$VERSION
echo "Pushed ghcr.io/$GITHUB_USER/$APP:$VERSION"
אחר כך chmod +x publish.sh, ותריצו ./publish.sh v1.2.3. 30 שניות, וה-image ב-registry.
הבעיה שלא סיפרו לכם: Apple Silicon (arm64) מול VPS (amd64)
זו אחת הבעיות הנפוצות ביותר בקרב Vibe Coders ישראלים, כי Apple Silicon (M-series Macs) נפוצים מאוד בארץ ו-VPS זולים רובם amd64. התסמין: בניתם image על הלפטופ, דחפתם ל-ghcr.io, ה-VPS מושך אותו, מנסה להריץ, ומיד מקבל:
exec /app/start.sh: exec format error
ההסבר: image הוא לא רק רשימת קבצים, הוא גם binary שתואם לארכיטקטורת CPU מסוימת. אם ה-base image הוא linux/arm64 (Apple Silicon), ה-binaries בתוכו הם arm64. ה-VPS שלכם (Hetzner/DO/Vultr/Contabo ברירת מחדל) הוא amd64 — ומעבד amd64 לא יכול להריץ binary של arm64. ה-API של המערכת קוראת לזה exec format error, וזה סימן ברור שהארכיטקטורה לא תואמת.
ב-Docker Desktop ב-Mac יש שתי אפשרויות: "Use Rosetta for x86_64/amd64 emulation" (איטי מאוד) או "Use Virtualization Framework with default" (ברירת מחדל, רץ native arm64). ברירת המחדל רץ native arm64 — מה שאומר שאם אתם בונים image על Mac בלי לציין platform, ה-image יהיה arm64, ולא ירוץ על amd64 VPS.
התיקון המהיר: --platform linux/amd64
אם ה-VPS שלכם הוא amd64, פשוט תגידו ל-Docker לבנות image של amd64 גם על Mac:
docker build --platform linux/amd64 -t myapp:v1 .
docker push ghcr.io/YOUR_USER/myapp:v1
זה עובד, אבל דורש ש-Docker יריץ emulation (QEMU) שלוקחת יותר זמן. תצפו ל-build איטי פי 2-3 מהרגיל, במיוחד ב-install של תלויות.
הפתרון המקצועי: multi-arch עם buildx
buildx היא גרסה מורחבת של docker build שמגיעה עם Docker Desktop ותומכת בבנייה למספר ארכיטקטורות בו-זמנית. הפקודה:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/YOUR_USER/myapp:v1 \
--push \
.
מה קורה מאחורי הקלעים: buildx בונה שני images נפרדים (amd64 ו-arm64), יוצר OCI manifest list (קובץ JSON שמפנה לשני ה-images), ודוחף את הכל ל-registry. כש-VPS amd64 ימשוך את myapp:v1, ה-registry יתן לו את ה-image של amd64. כש-Mac arm64 ימשוך אותו, הוא יקבל את ה-arm64. אותו tag, שתי גרסאות, אפס התערבות ידנית.
הערה חשובה: buildx לא יכול לדחוף את ה-image לתוך ה-registry המקומי שלכם (docker desktop) בזמן multi-arch build. הוא דורש --push או --load (שטוען רק ל-local, ולכן רק ארכיטקטורה אחת). לכן ה-flow הסטנדרטי הוא: buildx עם --push ישירות ל-registry.
החלק הטוב: ב-build השני והלאה, buildx משתמש ב-cache. אם שיניתם רק שורה אחת בקוד, הוא יבנה רק את ה-layers שהשתנו עבור שתי הארכיטקטורות. זה מהיר משמעותית מ-build ראשון.
טיפ ל-Vibe Coders: אם אתם יודעים שה-VPS שלכם amd64, תשמרו את ה-platform ב-docker-compose.yaml או ב-Dockerfile עצמו עם FROM --platform=linux/amd64 node:20-slim. ככה לא תצטרכו לזכור לציין את זה בכל build.
זה קורה לכולם לפחות פעם אחת. ה-flow הקלאסי: בניתם image על Mac, עשיתם docker save, העתקתם את ה-tar ל-VPS, עשיתם docker load, docker run, קיבלתם exec format error. ההפתעה: לא הייתם צריכים לעבור דרך registry בכלל — רק docker build --platform linux/amd64 ב-build הראשון. התיקון: docker buildx build --platform linux/amd64,linux/arm64 -t ... --push . פעם אחת, ושתי המכונות מרוצות.
Deploy ידני ל-VPS דרך SSH — docker compose up -d
הדרך הכי פשוטה להריץ את האפליקציה שלכם בעולם היא: VPS + SSH + docker compose. שלושה צעדים, עובד בכל מקום, ומתאים לתקציב של 4-6 דולר לחודש ל-VPS קטן.
VPS (Virtual Private Server) הוא מכונה וירטואלית שמושכרת מספק ענן. ספקים פופולריים ל-Vibe Coders ישראלים: Hetzner (גרמניה, זול, אמין), DigitalOcean (ארה"ב/אירופה, ממשק נוח), Vultr (ארה"ב/אירופה/אסיה, מגוון מיקומים), Contabo (זול מאוד, ביצועים סבירים). כולם מציעים VPS ב-4-6 $/חודש שמריץ 2-4 containers קטנים בלי בעיה.
אחרי שיצרתם VPS וקיבלתם כתובת IP + סיסמה (או הוספתם את ה-SSH key שלכם), הצעד הראשון הוא להתחבר:
ssh root@YOUR_VPS_IP
# או עם key: ssh -i ~/.ssh/id_rsa root@YOUR_VPS_IP
בתוך ה-VPS, צריך לוודא ש-Docker מותקן. ברוב ה-VPSים מגיעים בלי Docker, אז תצטרכו להתקין:
# אובונטו/דביאן — הפקודה הרשמית של Docker
curl -fsSL https://get.docker.com | sh
# אחרי ההתקנה, ודאו ש-docker compose עובד
docker compose version
docker --version
עכשיו, העתיקו את הקבצים מהלפטופ. יש 3 שיטות:
שיטה 1: rsync (מומלץ). מהלפטופ:
rsync -avz --exclude 'node_modules' --exclude '.git' \
./project root@YOUR_VPS_IP:/app
שיטה 2: scp. מהלפטופ:
scp -r ./project root@YOUR_VPS_IP:/app
שיטה 3: git clone (הכי נקי). ב-VPS:
cd /app
git clone https://github.com/YOUR_USER/YOUR_REPO.git .
השיטה השלישית היא הסטנדרד לפרודקשן — קוד תמיד מגיע מ-git, לא מהלפטופ. אחרי git clone, ב-VPS:
cd /app
cp .env.example .env # אם צריך
# ערכו את .env עם nano/vim — סיסמאות, API keys וכו'
nano .env
docker compose pull # מושך את ה-images העדכניים מ-registry
docker compose up -d # מריץ ברקע (detached)
ה-3 פקודות האחרונות הן ה-deploy המלא. pull מושך את ה-image החדש מ-ghcr.io, up -d מפעיל את כל ה-services ברקע. ברירת המחדל של compose היא --remove-orphans — containers ישנים שלא קיימים ב-compose.yaml נמחקים. אם ה-image השתנה, compose יזהה את זה ויצור container חדש עם ה-image החדש.
אחרי ה-deploy, 3 פקודות לבדיקה:
docker compose ps # רואה אילו services רצים
docker compose logs -f web # רואה לוגים של service ספציפי
curl http://YOUR_VPS_IP # בודק שהאפליקציה עונה
אם הכל עובד, האפליקציה זמינה ב-http://YOUR_VPS_IP:PORT. זהו. בלי HTTPS עדיין, בלי domain, אבל היא חיה ורצה בעולם.
למה compose up -d, ולא docker run
אפשר גם docker run ידני, אבל אז תצטרכו לנהל volumes, networks, ו-env vars בעצמכם. compose.yaml שכבר בניתם בפרק 5 הוא תיעוד חי של איך האפליקציה צריכה לרוץ. docker compose up -d מיישם אותו בלי להמציא מחדש. זה גם הסיבה שבגללה השקענו בפרק 5 — הקובץ הזה הופך deploy מ"פעולה ידנית מסובכת" ל"פקודה אחת".
Coolify — PaaS קוד-פתוח עם HTTPS אוטומטי
deploy ידני עובד, אבל הוא דורש שתזכרו לעשות git pull, docker compose pull, docker compose up -d בכל פעם שתרצו לעדכן. Coolify פותר את זה. Coolify (coolify.io) הוא PaaS (Platform as a Service) קוד-פתוח ולאחסון-עצמי שמתחרה ב-Heroku, Vercel, ו-Railway — אבל במקום להיות מתארח בענן של מישהו אחר, הוא רץ על ה-VPS שלכם.
בגרסה 4.0 (נכון לאמצע 2026), Coolify תומך ביותר מ-280 שירותים מוכנים מראש (Postgres, Redis, MySQL, MongoDB, Minio, Keycloak, ועוד) ובכל image שתדחפו אליו דרך Docker. הוא מנהל את ה-HTTPS עם Let's Encrypt אוטומטית (certbot מובנה), מנהל DNS, מציג לוגים, ומאפשר rollback לגרסאות קודמות.
ההתקנה
Coolify מתקין את עצמו על VPS נקי (Ubuntu/Debian) בפקודה אחת:
ssh root@YOUR_VPS_IP
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
הסקריפט לוקח 5-10 דקות. בסוף הוא ידפיס URL ופורט (ברירת מחדל 8000) לגישה לממשק ה-web. http://YOUR_VPS_IP:8000, ואתם בפנים. הצעד הראשון בממשק: צרו חשבון admin (ה-local user, לא קשור ל-GitHub).
Deploy של אפליקציה
ה-flow הוא 5 צעדים בלבד:
- חברו את ה-Git repo: Settings → GitHub → התחברו עם OAuth. תנו ל-Coolify גישה ל-repo הספציפי.
- צרו "Resource" חדש: Project → New Resource → Application → Public/Private Repository. בחרו את ה-repo.
- הגדירו build: Build Pack = "Dockerfile". Port = 3000 (או מה שהאפליקציה שלכם מאזינה עליו).
- הגדירו env vars: בטאב "Environment Variables" הוסיפו את כל המשתנים מה-
.envשלכם (DATABASE_URL, SECRET_KEY, וכו'). - Deploy: לחצו Deploy. Coolify יבנה את ה-image, יריץ את ה-container, יקבל domain, ויפעיל HTTPS.
אחרי ה-deploy הראשון, כל push ל-main ב-GitHub יגרום ל-Coolify לבנות מחדש ולעדכן את ה-container. CI/CD אוטומטי בלי לכתוב שורה של GitHub Actions. הוא גם ינפיק SSL של Let's Encrypt תוך 30-60 שניות, אוטומטית, בלי שתצטרכו להגדיר certbot.
היתרון הגדול מול deploy ידני
ב-deploy ידני, עדכון דורש SSH + 3 פקודות + תקווה שלא תשברו משהו. ב-Coolify, אתם לוחצים "Deploy" בממשק (או עושים git push) והוא עושה הכל: בנייה, rollback במקרה של כישלון, ניהול secrets, ניהול volumes, ניהול DNS, ו-HTTPS. המחיר: VPS חזק יותר (מינימום 2GB RAM, $6-12/חודש), ועקומת למידה ראשונית של 1-2 שעות.
טיפ חשוב: Coolify עצמו רץ בתוך container על ה-VPS. הוא לא מתנגש עם ה-applications שלכם — הוא משתמש ב-Docker Engine של ה-host, אבל עם namespaces נפרדים. אפשר להריץ את Coolify ועוד 3-4 אפליקציות על VPS 4GB בלי בעיה.
סריקת אבטחה לפני go-live: docker scout cves
עכשיו, לפני שהאפליקציה שלכם חיה בעולם, כדאי לעשות סריקת אבטחה מהירה אחת. docker scout cves היא פקודה שמגיעה עם Docker Desktop ובודקת את ה-image שלכם מול מאגר CVE (Common Vulnerabilities and Exposures) ידועים. היא בונה SBOM (Software Bill of Materials) — רשימה מלאה של כל החבילות ב-image, עם הגרסאות שלהן — ומסמנת חולשות ידועות בכל אחת.
docker scout cves myapp:v1
הפלט יראה טבלה עם עמודות: Package, Version, CVE, Severity. ה-Severity הוא העמודה החשובה ביותר — Low, Medium, High, או Critical. רוב ה-images של אפליקציות ריאקט/נוד יראו 0-3 חולשות High ב-base image (node:20-slim, python:3.12-slim) — בדרך כלל חולשות ב-glibc, openssl, או ספריות C. התיקון ב-90% מהמקרים: עדכון ה-base image לגרסה חדשה יותר.
למשל, אם אתם רואים "8 Critical CVEs in node:20-slim", התיקון הוא:
# ב-Dockerfile, שנו את השורה הראשונה מ:
FROM node:20-slim
# לגרסה העדכנית ביותר של אותו tag:
FROM node:20.18-slim
# או החליפו ל-tag ספציפי שמתוחזק:
FROM node:20-bookworm-slim
תיקון אחד כזה יכול לסגור 5-10 חולשות ב-Critical ו-High. אחרי התיקון, בנו מחדש, הריצו שוב את docker scout cves, ותראו את ההפרש.
טיפ חשוב: docker scout לא חובה לפני deploy, אבל זו הרגל טוב. אם תעשו את זה על 5 ה-images הראשונים שלכם, תלמדו לזהות דפוסים — איזה base images נקיים יותר, איזה ספריות מביאות חולשות, ואיפה ה-attack surface הגדול ביותר שלכם. המטרה היא לא "0 CVEs" (בלתי אפשרי בעולם התוכנה), אלא "מודעות למה שיש ב-image שלי לפני שהוא חשוף לאינטרנט".
"אין לי נתוני לקוחות, רק תוכן ציבורי" — גם אז, האקר יכול לנצל חולשה ב-base image כדי לקבל גישת root ל-VPS שלכם, ומשם להפעיל mining של קריפטו, להשתמש ב-VPS שלכם כבוטנט, או לתקוף רשתות אחרות דרכו. כל container שמאזין בפורט ציבורי הוא מטרה. docker scout cves לוקח 30 שניות. אין סיבה לדלג.
Capstone — 'מסמך הרצה-בכל-מקום'
הגענו לרגע המסכם. הקורס הזה נבנה כדי שבסופו תהיה לכם אפליקציה שרצה בכל מקום, והדרך להבטיח שזה יקרה — גם אחרי שתשכחו 80% מהפקודות — היא מסמך הרצה-בכל-מקום. מסמך אחד, עמוד אחד, שמתעד את כל מה שצריך כדי להריץ את האפליקציה שלכם בכל סביבה. הוא ה-output של הקורס, ה-deliverable הסופי.
תבנית המסמך
# מסמך הרצה-בכל-מקום — [שם האפליקציה]
תאריך עדכון אחרון: 2026-06-23
## Image
- Registry: ghcr.io/YOUR_USER/myapp
- Tag: v1.0.0
- Platforms: linux/amd64, linux/arm64
- Digest: sha256:abc123... (אופציונלי, ל-pinning מוחלט)
## Environment Variables (חובה ב-runtime, לא ב-image)
- DATABASE_URL=postgres://user:pass@db:5432/myapp
- SECRET_KEY=...
- PORT=3000
- NODE_ENV=production
## Volumes (חייבים לשרוד restart)
- pgdata:/var/lib/postgresql/data (Postgres data)
- /etc/letsencrypt (Coolify certs)
## Build
docker buildx build --platform linux/amd64,linux/arm64 \
-t ghcr.io/YOUR_USER/myapp:v1.0.0 --push .
## Run (מקומי)
docker compose up -d
curl http://localhost:3000 # בדיקה
## Deploy (production)
# אופציה A: SSH ידני
ssh root@YOUR_VPS_IP
cd /app && git pull && docker compose pull && docker compose up -d
# אופציה B: Coolify
git push origin main # Coolify בונה ומעדכן אוטומטית
## בריאות
- URL: https://myapp.example.com
- Health: curl -f http://localhost:3000/health
- Logs: docker compose logs -f
## גרסאות קודמות (rollback)
- v1.0.0 ← גרסה נוכחית
- v0.9.5 ← גרסה קודמת (rollback: docker compose pull && docker compose up -d עם tag ישן)
## סריקת אבטחה אחרונה
docker scout cves ghcr.io/YOUR_USER/myapp:v1.0.0
# תוצאה: 0 Critical, 2 High, 14 Medium — ה-High ב-glibc, מתוקנן ב-v1.0.1
זה המסמך. שמרו אותו ב-GitHub repo של האפליקציה בשם RUN-ANYWHERE.md או DEPLOY.md. כל מי שירצה להריץ את האפליקציה שלכם — אתם בעוד חצי שנה, חבר, לקוח, או קולגה — יצטרך רק את הקובץ הזה. בלי זה, הידע נמצא רק בראש שלכם. בראש שלכם, זה הולך לאיבוד תוך חודש.
ה-checklist האמיתי לפני deploy
לפני שאתם דוחפים את ה-image לעולם, עברו על הרשימה הבאה:
- image tag לא :latest. תמיד גרסה ספציפית (v1.0.0, 2026-06-23).
- אין סודות ב-image.
docker history myapp:v1לא צריך להראות שוםENV API_KEY=.... אם כן —docker scout recommendationsיציע תיקון. - משתמש non-root.
docker exec -it myapp whoamiצריך להחזיר משהו אחר מ-root. - multi-arch build. אם יש לכם Mac או שרת arm64 —
docker buildx build --platform linux/amd64,linux/arm64. - docker scout cves. 0 Criticals, או הצדקה מתועדת לכל אחד.
- volume קריטי מתועד. בלי
pgdata:/var/lib/postgresql/dataב-compose.yaml, המסד ימחק ב-redeploy. - healthcheck קיים. לא רק
depends_on— צריך גם healthcheck אמיתי.
7 סעיפים. אחרי שתעברו אותם 3-4 פעמים, הם יהפכו לאוטומטיים. ב-deploy הראשון, הם ייקחו לכם 20 דקות. אחרי 5 deploys, 5 דקות.
ואיפה ממשיכים מכאן
הקורס הזה נגמר. יש לכם אפליקציה שרצה בכל מקום, אבל זו רק ההתחלה. הנושאים שלא כיסינו — ושהם ה-sequel הטבעי של הקורס הזה — הם: reverse proxy (Caddy, Nginx, Traefik — שמנהלים HTTPS termination, rate limiting, ו-caching); observability (logs מרכזיים, metrics, traces — כדי לדעת מה האפליקציה עושה גם כשהיא לא קורסת); backups אוטומטיים (snapshot של ה-volume של ה-Postgres ל-S3 פעם ביום); CI/CD מלא (GitHub Actions שבונה, בודק, ודוחף אוטומטית); security hardening (AppArmor, seccomp, rootless Docker). אלה הקורסים הבאים.
אבל לפני הכל — חגגו. לקחתם אפליקציה שרצה רק על הלפטופ, ארזתם אותה לתוך image, חיברתם למסד-נתונים, בניתם multi-arch, דחפתם ל-registry, ופרסתם לשרת אמיתי. זה הישג. רוב האנשים שמתחילים ללמוד Docker לא מגיעים לכאן גם אחרי שנה.
מסמך ההרצה-בכל-מקום שלכם מוכן. הקורס הסתיים. תתחילו לבנות.
המטרה: לתרגל את 5 הפקודות מהסעיף "ערכת האבחון" על container אמיתי שקורס. הרעיון: ליצור כשל בכוונה, ואז לאבחן אותו בלי למחוק את ה-container.
מה תעשו:
- צרו תיקייה ריקה, ובתוכה Dockerfile מינימלי:
FROM node:20-slim WORKDIR /app COPY . . CMD ["node", "server.js"] - צרו
server.jsשמנסה לקרוא env var חסר:const port = process.env.PORT; if (!port) { console.error('PORT is not set!'); process.exit(1); } console.log('Running on port', port); - בנו והריצו בלי env var:
docker build -t broken:1 . docker run --name broken-container broken:1 - ה-container יצא תוך שנייה. עכשיו אבחון:
docker ps -a | grep broken— ראו את exit code 1.docker logs broken-container— ראו את "PORT is not set".docker inspect broken-container | grep -A 3 "Env"— ראו שה-env ריק.docker run --rm -it --entrypoint sh broken:1— היכנסו לתוך ה-container, הריצוnode server.jsידנית, וראו את אותה שגיאה.
- עכשיו תקנו:
docker run -e PORT=3000 --name fixed-container broken:1. ה-container רץ.
תוצאה צפויה: אחרי התרגיל, תדעו להגיד לכל container שקורס "אני יודע בדיוק איפה להסתכל". הרגל שתקחו איתכם: כש-container נופל, תמיד מתחילים ב-docker ps -a + docker logs לפני שמנסים לתקן.
המטרה: לקחת את ה-image של האפליקציה שלכם, לתייג אותו בפורמט ghcr.io, ולדחוף ל-registry של GitHub. זה הצעד הראשון לכל deploy.
מה תעשו:
- צרו Personal Access Token ב-GitHub: Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token. סקופ:
write:packages,read:packages,repo. - שמרו את הטוקן במשתנה סביבה זמני:
export CR_PAT=ghp_xxxxxxxxxxxx. - Login:
echo $CR_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin. אמורים לראות "Login Succeeded". - תיוג:
docker tag myapp:v1 ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1. - דחיפה:
docker push ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1. המתינו לסיום (תלוי בגודל ה-image, 30 שניות עד 5 דקות). - אימות: פתחו https://github.com/YOUR_GITHUB_USERNAME?tab=packages — ה-image צריך להופיע שם.
- משיכה חזרה (לוודא שהוא באמת נגיש):
docker pull ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1.
תוצאה צפויה: image בגודל 100-300MB נדחף בהצלחה, מופיע ב-github.com/packages, וניתן למשוך אותו בחזרה. אם אתם רואים "denied" — הטוקן לא כולל write:packages, או ה-username לא תואם לבעלים של ה-registry. אם אתם רואים "requested access denied" — צריך לאשר את ה-package ב-GitHub (הודעה תישלח אליכם במייל).
המטרה: לבנות image שירוץ גם על ה-Mac שלכם (arm64) וגם על VPS amd64. אחרי התרגיל, תוכלו לדחוף אותו image ל-registry ולהריץ אותו בכל מקום בלי "exec format error".
מה תעשו:
- ודאו שאתם בתיקיית הפרויקט שלכם, שיש Dockerfile תקין ושאתם logged-in ל-ghcr.io (מתרגיל 2).
- צרו builder חדש (אם עוד לא עשיתם):
docker buildx create --name multiarch --driver docker-container --use docker buildx inspect --bootstrap - בנו multi-arch:
docker buildx build \ --platform linux/amd64,linux/arm64 \ -t ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1 \ --push \ . - המתינו. הבילד הראשון ייקח 5-15 דקות (emulation איטית). הבילדים הבאים יהיו מהירים יותר בזכות ה-cache.
- אימות:
docker buildx imagetools inspect ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1— אמורים לראות שני manifests:linux/amd64ו-linux/arm64.
תוצאה צפויה: image יחיד ב-registry שמכיל שתי גרסאות. מי שמושך מ-Mac יקבל arm64, מי שמושך מ-VPS יקבל amd64. אין יותר "exec format error".
המטרה: לקחת את ה-image מ-ghcr.io, להריץ אותו על VPS אמיתי, ולוודא שהוא זמין ב-IP ציבורי.
מה תצטרכו: VPS (גודל מינימלי, 2GB RAM), כתובת IP, גישת SSH. אם אין לכם VPS, אפשר להריץ את התרגיל ב-Docker Desktop על הלפטופ עם docker run במקום deploy אמיתי.
מה תעשו:
- התחברו ל-VPS:
ssh root@YOUR_VPS_IP. - התקינו Docker:
curl -fsSL https://get.docker.com | sh docker --version docker compose version - צרו את התיקייה ו-clone:
mkdir -p /app && cd /app && git clone https://github.com/YOUR_USER/YOUR_REPO.git . - צרו .env:
cp .env.example .env && nano .env(מלאו את הערכים האמיתיים). - Login ל-ghcr.io:
echo $CR_PAT | docker login ghcr.io -u YOUR_USER --password-stdin(אותו טוקן מתרגיל 2). - משכו והריצו:
docker compose pull docker compose up -d docker compose ps - בדיקה:
curl http://localhost(מה-VPS). אם אתם רואים HTML — האפליקציה חיה. עכשיוcurl http://YOUR_VPS_IPמהלפטופ. - ראו לוגים:
docker compose logs -f --tail 100.
תוצאה צפויה: אפליקציה חיה בכתובת ה-IP של ה-VPS, נגישה מהדפדפן בכל מקום בעולם. בלי HTTPS, בלי domain, אבל חיה ורצה. אם לא עובד — docker compose logs יראה לכם למה.
שתי שיטות deploy, שתי אסטרטגיות שונות. השאלה "איזו מתאימה לי" תלויה ב-4 קריטריונים:
- אם יש לכם רק אפליקציה אחת ואתם לא רוצים לנהל UI נוסף → SSH +
docker compose up -d. פשוט, מהיר, אפס תלויות. חסרון: כל עדכון דורש 3 פקודות ידניות. - אם יש לכם 2+ אפליקציות או שאתם רוצים HTTPS אוטומטי בלי להגדיר Caddy/Nginx → Coolify. UI נוח, CI/CD אוטומטי, Let's Encrypt מובנה. חסרון: דורש VPS חזק יותר (2GB RAM מינימום).
- אם אתם עובדים בצוות של 2+ אנשים וצריכים שליטת גישה → Coolify. הוא תומך ב-multi-user עם roles, מה ש-
docker composeעל VPS יחיד לא נותן בלי עבודה ידנית. - אם הלקוח שלכם לא-טכני ורוצה ללחוץ "Deploy" בעצמו → Coolify בלי ספק. ה-UI שלו בנוי בדיוק עבור "מישהו שלא מבין Docker".
- אם התקציב חשוב יותר מכל → SSH על Hetzner ב-4 €/חודש. Coolify דורש לפחות 6-8 €/חודש של VPS גדול יותר.
המבחן הסופי: כמה פעמים בחודש אתם מתכוונים לעדכן? פעם-פעמיים → SSH מספיק. 5+ פעמים → Coolify משתלם.
ה-container רץ על ה-VPS, אבל האפליקציה לא עונה. מאיפה מתחילים? סדר פעולות שמבוסס על שכיחות הבעיות ב-deploys אמיתיים:
docker compose ps— האם ה-container ב-status "Up"? אם לא — חזרו לסעיף 1 (exits immediately).docker compose logs --tail 50— מה האפליקציה אומרת? חפשו Error, FATAL, "cannot connect", "ECONNREFUSED".docker compose ps+docker inspect <id> | grep IPAddress— האם ה-container קיבל IP? אם לא — בעיית רשת, כנראה לא הגדרתם port mapping נכון.curl http://localhost:PORTמה-VPS — האם האפליקציה עונה מקומית? אם כן — הבעיה היא firewall של ה-VPS. אם לא — האפליקציה עצמה לא רצה.docker exec -it <id> sh— היכנסו לתוך ה-container. תריצו את הפקודה שמפעילה את האפליקציה ידנית. תראו את השגיאה האמיתית.docker scout cves myapp:v1— לפעמים הבעיה היא באבטחה. נדיר, אבל קורה.
חוק אצבע: 80% מהבעיות נפתרות בצעד 1-3. רק 5% מגיעות לצעד 5-6.
השאלה "האם לעדכן את node:20-slim לגרסה חדשה יותר" נשאלת בכל פעם ש-Docker Scout מראה חולשות. הכללים:
- אם רואים Critical CVE ב-base image → עדכנו מיד. גם אם זה דורש rebuild של הכל. הסיכון גבוה מדי.
- אם רואים High CVE שלא משפיע על הקוד שלכם (למשל חולשה בתוכנה שלא בשימוש) → תעדכנו בעדכון הבא. לא דחוף.
- אם רואים Medium/Low CVEs רבים → עדכון כל 3-6 חודשים. לא קריטי, אבל תורם ליציבות.
- אם ה-base image עבר EOL (end-of-life) → עדכון חובה.
node:16,python:3.9,php:7.4— כולם כבר לא מתוחזקים. עדכון ל-LTS הבא. - אם אתם מתחילים פרויקט חדש → תמיד הגרסה העדכנית ביותר. גם אם היא "לא יציבה" — תוך חודש היא תהפוך ליציבה.
חוק אצבע: תעדכנו base image אחת לרבעון בערך, או מיד כשיש Critical CVE. לא לפני, לא אחרי.
הקורס הסתיים, אבל העבודה עם Docker לא. הנה השגרה השבועית שתשמור על האפליקציה שלכם בריאה גם חודשים אחרי ה-deploy הראשון:
יומי (5 דקות, בתחילת היום):
docker compose psעל ה-VPS — ודאו שכל ה-services עדיין רצים.- סריקה מהירה של
docker compose logs --since 1h --tail 20— האם יש שגיאות חדשות?
שבועי (30 דקות, יום ראשון):
docker system df— כמה דיסק תופסים ה-images ו-containers. אם מעל 80% —docker system prune(בזהירות, אחרי שאתם מוודאים שאין volumes חשובים).docker scout cves myapp:current— האם הופיעו חולשות חדשות?- אם עברו 30+ ימים —
docker compose pull && docker compose up -dכדי למשוך גרסאות חדשות של images בסיס.
חודשי (60 דקות, סוף חודש):
- בדיקת backups — אם הוספתם סקריפט שמגבה את ה-volume של Postgres ל-S3, ודאו שהוא רץ ושהגיבויים תקינים.
- עדכון base images — רוץ על
docker scout quickview myapp:currentוהחלט אם לעדכן. - בדיקת עלויות — ודאו שאתם לא משלמים על VPS שלא בשימוש או על domain שלא צריך.
רבעוני (3 שעות, כל סוף-רבעון):
- ארכיטקטורה — האם ה-stack עדיין מתאים? האם צריך להוסיף cache (Redis)? האם ה-VPS מספיק חזק?
- Runbook — עדכן את RUN-ANYWHERE.md. גרסאות, env vars, volumes. אם משהו השתנה ולא תיעדתם, בעוד חצי שנה תשכחו.
- תרגול rollback — בצע deploy של גרסה ישנה, ואז חזרה לחדשה. ודאו שאתם יודעים איך לחזור אחורה ב-5 דקות.
העיקרון: Docker זה לא "פרויקט חד-פעמי". זו תשתית חיה שדורשת תחזוקה שוטפת. השגרה הזו הופכת "תקלה ב-3 לפנות בוקר" ל"תיקון של 10 דקות ביום ראשון".
ברירת המחדל של רוב ה-VPSים: כל הפורטים פתוחים. זה אומר שאם הרצתם Postgres על port 5432 בלי סיסמה חזקה, כל סורק פורטים באינטרנט ימצא אותו תוך דקות. הפתרון: ufw allow 22 (ל-SSH), ufw allow 80 ו-ufw allow 443 (ל-web), ufw enable. כל השאר סגור. ה-port של ה-database לא צריך להיות חשוף לאינטרנט בכלל — רק ל-container השני שמדבר איתו דרך הרשת הפנימית של Docker.
depends_on: condition: service_healthy ואז לקבל "connection refused" ב-startup
תרחיש קלאסי: הוספתם Postgres ל-compose.yaml, האפליקציה שלכם מתחילה מיד, מנסה להתחבר ל-db, מקבלת "ECONNREFUSED" כי ה-db עוד לא מוכן. בלי healthcheck על ה-db ו-depends_on: condition: service_healthy באפליקציה, הקריסה הראשונה תהיה 1-2 דקות אחרי כל deploy. זה היה תרגיל בפרק 5, והוא חיוני גם בפרק 6. הוסיפו את ה-healthcheck על כל service שיש לו תלות ב-service אחר.
ה-VPS קרס. צריך לבנות אותו מחדש. יש לכם image ב-ghcr.io, יש לכם compose.yaml, אבל... איזה ערך היה ל-SECRET_KEY? מה היה DATABASE_URL? אם ה-env vars היו רק ב-VPS (בלי תיעוד), אתם נעולים החוצה. הפתרון: RUN-ANYWHERE.md הוא חובה, לא אופציה. רשמו שם את השמות של ה-env vars ואת המקור של הערכים (1Password, Bitwarden, AWS Secrets Manager). את הערכים עצמם אל תכתבו ב-git.
אם תיקחו רק פעולה אחת מהפרק הזה — שתהיה זאת: כתבו היום את RUN-ANYWHERE.md עבור האפליקציה שלכם. עמוד אחד, התבנית מהסעיף הקפיטולי. רשמו: image tag, env vars בלי ערכים, volumes קריטיים, פקודת deploy, ו-3 פקודות debug. שמרו את זה ב-git. כי בעוד 3 חודשים, כשתרצו לעדכן את האפליקציה או לפרוס מחדש — אתם לא תזכרו. הקובץ הזה יזכור בשבילכם. זה ההבדל בין "פרויקט שעובד היום" לבין "פרויקט שעובד גם בעוד שנה, אחרי שהקשר שלכם עם ה-stack התקרר".
- שאלה: container יצא עם exit code 137. מה הסיבה הכי סבירה, ואיך מאבחנים?
תשובה: 137 = SIGKILL = 128 + 9. הסיבה הכי סבירה: OOM killer של Linux הרג את ה-process כי חרג ממכסת ה-RAM. איך מאבדחנים:dmesg | grep -i "killed process"על ה-host, אוdocker inspect <id> | grep -i memory. הפתרון:docker run --memory 1gאו אופטימיזציה של האפליקציה. - שאלה: מה ההבדל בין Docker Hub ל-ghcr.io עבור Vibe Coder שעובד לבד?
תשובה: Docker Hub ציבורי דורש login אחרת מוגבל ל-100 pulls/6 שעות לכל IP (ובעייתי ב-CI/משרד משותף). ghcr.io חינמי עם חשבון GitHub, יושב ליד ה-repository, ובעל מכסה גבוהה בהרבה לשימוש סביר. ל-Vibe Coder שכבר על GitHub — ghcr.io היא ברירת המחדל. - שאלה: למה צריך buildx ו-multi-arch? מה התסמין אם לא?
תשובה: image שנבנה על Mac M-series (arm64) לא ירוץ על VPS amd64 וייתן "exec format error". buildx בונה שני images (amd64 + arm64), יוצר OCI manifest list, ודוחף ל-registry. ה-VPS ימשוך את ה-amd64, ה-Mac ימשוך את ה-arm64, ואותו tag עובד בשתי המכונות. - שאלה: מה 3 הצעדים של deploy ידני ל-VPS דרך SSH?
תשובה: אחרי ש-git clone הועתק ו-.env מולאו: (1)docker login ghcr.io, (2)docker compose pull, (3)docker compose up -d. הראשון מאמת את הזהות, השני מושך את ה-image החדש מ-registry, השלישי מריץ את ה-container החדש. - שאלה: מה ההבדל בין deploy ידני (SSH) ל-Coolify? מתי כדאי להשתמש בכל אחד?
תשובה: SSH = 3 פקודות ידניות אחרי כל push, מתאים לפרויקט יחיד עם deploys נדירים. Coolify = UI שמנהל CI/CD אוטומטי + HTTPS של Let's Encrypt + ניהול multi-app + גלגול לאחור (rollback), מתאים ל-2+ אפליקציות או deploys תכופים או לקוחות לא-טכניים. החסרון של Coolify: דורש VPS חזק יותר (2GB+ RAM).
- Container שיוצא מיד = תהליך ראשי סיים. Docker זקוק ל-PID 1 ב-foreground. אם הוא סיים — ה-container מת, בלי קשר לסיבה.
docker ps -a+docker logsתמיד מראים למה. - 5 פקודות debug מספיקות ל-95% מהבעיות:
ps -a,logs,inspect,exec, ו-override של entrypoint. הסדר חשוב — התחילו מהזול. - ghcr.io עדיף על Docker Hub ל-Vibe Coders. חינם עם חשבון GitHub, מכסה גבוהה, private repos ללא תשלום, native integration עם Actions.
- Docker Hub limits הם לפי IP, לא לפי משתמש. בבית לא בעיה. במשרד/CI — בעיה תוך שבוע.
docker loginאו ghcr.io פותרים. - exec format error = ארכיטקטורה לא תואמת. buildx עם multi-arch פותר את זה פעם אחת, ואז שתי המכונות מרוצות מאותו tag.
- deploy ידני = 3 פקודות: clone, pull, up. זה עובד בכל סביבה, בלי תלויות, ומתאים לפרויקטים קטנים.
- Coolify = Heroku חינמי על ה-VPS שלכם. CI/CD + HTTPS + multi-app + UI נוח. משתלם כשיש 2+ אפליקציות או deploys תכופים.
- RUN-ANYWHERE.md הוא ה-deliverable האמיתי של הקורס. עמוד אחד, תבנית קבועה, נשמר ב-git. בלי זה, הידע הולך לאיבוד תוך חודש.
- ☐ הרצתי container שקורס, איבחנתי אותו עם
ps -a+logs+exec, ותיקנתי. (תרגיל 1) - ☐ יצרתי Personal Access Token ב-GitHub עם
write:packages. - ☐ עשיתי
docker login ghcr.ioבהצלחה. - ☐ דחפתי image של האפליקציה שלי ל-ghcr.io עם tag ספציפי (לא
:latest). (תרגיל 2) - ☐ בניתי image multi-arch עם
docker buildxואימותי ששתי הארכיטקטורות ב-registry. (תרגיל 3) - ☐ הרצתי
docker scout cvesעל ה-image האחרון, ואין לי Critical CVEs פתוחים. - ☐ יש לי VPS (או גישה ל-Coolify) ועשיתי deploy של האפליקציה שלי בו. (תרגיל 4)
- ☐ האפליקציה נגישה ב-IP ציבורי / ב-Coolify, ואני רואה אותה בדפדפן.
- ☐ ה-volume של ה-database שורד restart של ה-container (
docker compose down && docker compose up -dוהמידע שם). - ☐ כתבתי
RUN-ANYWHERE.mdב-git של הפרויקט עם image tag, env vars (בלי ערכים), volumes, ופקודות deploy. - ☐ הגדרתי firewall ב-VPS (או ב-Coolify) שמגביל את הפורטים החשופים.
- ☐ אני מרגיש/ה שאני מוכן/ה לפרוס אפליקציה אמיתית בעצמי. אם לא — חוזר/ת לסעיף ה-debug (סעיף 2) ומתרגל/ת שוב.
- image ב-ghcr.io עם tag ספציפי (v1.0.0 או תאריך), multi-arch (amd64 + arm64), שנבדק עם
docker scout cvesואין לו Critical CVEs פתוחים. נגיש דרךdocker pullמכל מקום. - אפליקציה חיה ב-VPS או ב-Coolify — נגישה בכתובת IP ציבורית או דרך HTTPS, עם database ששורד restart, ועם healthcheck שמוודא שהיא באמת עונה.
- RUN-ANYWHERE.md ב-git repository של הפרויקט — עמוד אחד, תבנית קבועה, מתעד את ה-image tag, env vars (בלי ערכים רגישים), volumes, פקודות build/deploy/debug, וגרסאות קודמות ל-rollback.
- תרגול debug מתועד — צילום מסך או העתק פלט של 5 פקודות ה-debug (ps -a, logs, inspect, exec, override) על container שקרסתם בכוונה (תרגיל 1).
- 2 גרסאות ב-registry — v1.0.0 (יציב) ו-v1.1.0 (עדכון). הוכחה שאתם יודעים לדחוף גרסה חדשה בלי לשבור את הקודמת.
- security pass — פלט של
docker scout cvesעם החלטה מתועדת לכל CVE ברמת Medium+ (תיקון / דחייה עם נימוק).
זה היה הפרק האחרון של Docker בלי לפחד. עברתם מסע שלם: מהבנה של "למה האפליקציה לא רצה במקום אחר" — לבניית Dockerfile ו-image שעובד בכל ארכיטקטורה; מהבנה של image vs container — ל-compose שמחבר אפליקציה, מסד, ו-cache; מההתקנה הראשונה — ל-deploy חי על VPS עם HTTPS. יש לכם את כל מה שצריך כדי לארוז את האפליקציה שה-AI בנה ולהריץ אותה בכל מקום.
הקורס הסתיים, אבל Docker לא. הנושאים שלא כיסינו — ושהם הרחבה טבעית — הם VPS hardening (firewall, fail2ban, automatic security updates), observability (centralized logs, metrics, traces, alerts), CI/CD מלא (GitHub Actions שבונה ובודק אוטומטית), backups אוטומטיים (snapshots של volumes), ו-security hardening (AppArmor, seccomp, rootless). אלה הקורסים הבאים שמתבססים על מה שלמדתם כאן. אבל לפני הכל — חגגו, ותתחילו לבנות. בהצלחה.