6 שלב האינטגרציה

לאבחן, לשגר, ולהריץ בכל מקום — Registries, Multi-Arch, ו-Deploy ל-VPS

עד לפרק הזה יש לכם אפליקציה ארוזה ב-image, compose.yaml שמחבר אותה למסד-נתונים, ו-volume ששורד restart. החסרון היחיד: הכל עדיין רץ על הלפטופ. בפרק האחרון של הקורס נסגור את הלולאה: נלמד לאבחן container שקורס (כי זה תמיד קורס), לדחוף את ה-image ל-registry כדי שיהיה זמין לכל שרת בעולם, לבנות image שירוץ גם על Apple Silicon וגם על VPS amd64 (בלי "exec format error"), ולפרוס לשרת אמיתי — ידנית דרך SSH או דרך Coolify עם HTTPS אוטומטי. בסוף הפרק יש לכם את "מסמך הרצה-בכל-מקום" שמתעד איך להריץ את האפליקציה שלכם בכל מקום — ואיתו סוגרים את הקורס.

מה תוכלו לעשות אחרי הפרק הזה
לפני שמתחילים
הפרויקט שלך — איפה אנחנו במסלול

פרק 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 לפנות בוקר".

מתחיל+ 10 דקות debug תיאוריה+פרקטיקה

'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, ומצב. הכלים האלה הם ה"סטטוסקופ" שלכם — ובסעיף הבא נלמד אותם אחד-אחד.

בינוני 14 דקות CLI תרגול מעשי

ערכת האבחון: 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.

טעות נפוצה: להתעלם מ-exit code 137 כי "137 זה רק SIGKILL, זה תמיד תקין"

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, זה כמעט תמיד זיכרון.

מתחיל 10 דקות registry קונספט

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 Hubghcr.io
ברירת מחדל ב-docker runכן (אם לא צוין registry)לא — חייב prefix מלא
Pull limits לא-מחובר100 / 6 שעות לכל IPאין (rate limits נדירים)
Pull limits חשבון חינמי200 / 6 שעותגבוה מאוד, לרוב ללא הגבלה לשימוש סביר
private repos1 חינם, יותר = תשלוםללא הגבלה (עם חשבון GitHub חינמי)
שילוב עם GitHub Actionsדרך secretsnative — token אוטומטי ברוב flows
מומלץ ל-Vibe Coderרק ל-public images מוכריםכן, ברירת המחדל

ההבדל המעשי שכדאי לזכור: כל image שתדחפו ל-Docker Hub ציבורי יהיה גלוי לכולם באינטרנט, ואם הוא מכיל סודות (אסור, אבל זה קורה) — הם נחשפים. ב-ghcr.io, גם images "פרטיים" הם חינמיים, ותוכלו להגביל גישה ל-repos ספציפיים. זה חשוב במיוחד לאפליקציות של לקוחות.

מתחיל 8 דקות rate limits פתרון בעיות

מלכודת ה-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 — נתקלים תוך שבוע.

טעות נפוצה: להסתמך על Docker Hub ל-images של production בלי login

תרחיש קלאסי: בניתם 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 קריטיים לפרודקשן.

מתחיל 10 דקות CLI תרגול

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.

בינוני 12 דקות multi-arch Apple Silicon

הבעיה שלא סיפרו לכם: 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.

טעות נפוצה: לבנות image על Mac ולפרוס ישר על VPS בלי לבדוק platform

זה קורה לכולם לפחות פעם אחת. ה-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 . פעם אחת, ושתי המכונות מרוצות.

בינוני 10 דקות SSH deploy

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 מ"פעולה ידנית מסובכת" ל"פקודה אחת".

בינוני 10 דקות PaaS HTTPS

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 צעדים בלבד:

  1. חברו את ה-Git repo: Settings → GitHub → התחברו עם OAuth. תנו ל-Coolify גישה ל-repo הספציפי.
  2. צרו "Resource" חדש: Project → New Resource → Application → Public/Private Repository. בחרו את ה-repo.
  3. הגדירו build: Build Pack = "Dockerfile". Port = 3000 (או מה שהאפליקציה שלכם מאזינה עליו).
  4. הגדירו env vars: בטאב "Environment Variables" הוסיפו את כל המשתנים מה-.env שלכם (DATABASE_URL, SECRET_KEY, וכו').
  5. 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 בלי בעיה.

מתחיל 8 דקות security SBOM

סריקת אבטחה לפני 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 שניות. אין סיבה לדלג.

מתקדם 12 דקות capstone תיעוד

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 לעולם, עברו על הרשימה הבאה:

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 לא מגיעים לכאן גם אחרי שנה.

מסמך ההרצה-בכל-מקום שלכם מוכן. הקורס הסתיים. תתחילו לבנות.

תרגיל 1 — לאבחן container שקורס (15 דקות)

המטרה: לתרגל את 5 הפקודות מהסעיף "ערכת האבחון" על container אמיתי שקורס. הרעיון: ליצור כשל בכוונה, ואז לאבחן אותו בלי למחוק את ה-container.

מה תעשו:

  1. צרו תיקייה ריקה, ובתוכה Dockerfile מינימלי:
    FROM node:20-slim
    WORKDIR /app
    COPY . .
    CMD ["node", "server.js"]
  2. צרו 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);
  3. בנו והריצו בלי env var:
    docker build -t broken:1 .
    docker run --name broken-container broken:1
  4. ה-container יצא תוך שנייה. עכשיו אבחון:
    1. docker ps -a | grep broken — ראו את exit code 1.
    2. docker logs broken-container — ראו את "PORT is not set".
    3. docker inspect broken-container | grep -A 3 "Env" — ראו שה-env ריק.
    4. docker run --rm -it --entrypoint sh broken:1 — היכנסו לתוך ה-container, הריצו node server.js ידנית, וראו את אותה שגיאה.
  5. עכשיו תקנו: docker run -e PORT=3000 --name fixed-container broken:1. ה-container רץ.

תוצאה צפויה: אחרי התרגיל, תדעו להגיד לכל container שקורס "אני יודע בדיוק איפה להסתכל". הרגל שתקחו איתכם: כש-container נופל, תמיד מתחילים ב-docker ps -a + docker logs לפני שמנסים לתקן.

תרגיל 2 — לדחוף image ל-ghcr.io (20 דקות)

המטרה: לקחת את ה-image של האפליקציה שלכם, לתייג אותו בפורמט ghcr.io, ולדחוף ל-registry של GitHub. זה הצעד הראשון לכל deploy.

מה תעשו:

  1. צרו Personal Access Token ב-GitHub: Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token. סקופ: write:packages, read:packages, repo.
  2. שמרו את הטוקן במשתנה סביבה זמני: export CR_PAT=ghp_xxxxxxxxxxxx.
  3. Login: echo $CR_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin. אמורים לראות "Login Succeeded".
  4. תיוג: docker tag myapp:v1 ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1.
  5. דחיפה: docker push ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1. המתינו לסיום (תלוי בגודל ה-image, 30 שניות עד 5 דקות).
  6. אימות: פתחו https://github.com/YOUR_GITHUB_USERNAME?tab=packages — ה-image צריך להופיע שם.
  7. משיכה חזרה (לוודא שהוא באמת נגיש): 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 (הודעה תישלח אליכם במייל).

תרגיל 3 — build multi-arch ל-Mac ול-VPS (20 דקות)

המטרה: לבנות image שירוץ גם על ה-Mac שלכם (arm64) וגם על VPS amd64. אחרי התרגיל, תוכלו לדחוף אותו image ל-registry ולהריץ אותו בכל מקום בלי "exec format error".

מה תעשו:

  1. ודאו שאתם בתיקיית הפרויקט שלכם, שיש Dockerfile תקין ושאתם logged-in ל-ghcr.io (מתרגיל 2).
  2. צרו builder חדש (אם עוד לא עשיתם):
    docker buildx create --name multiarch --driver docker-container --use
    docker buildx inspect --bootstrap
  3. בנו multi-arch:
    docker buildx build \
      --platform linux/amd64,linux/arm64 \
      -t ghcr.io/YOUR_GITHUB_USERNAME/myapp:v1 \
      --push \
      .
  4. המתינו. הבילד הראשון ייקח 5-15 דקות (emulation איטית). הבילדים הבאים יהיו מהירים יותר בזכות ה-cache.
  5. אימות: 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".

תרגיל 4 — Deploy ל-VPS דרך SSH (25 דקות)

המטרה: לקחת את ה-image מ-ghcr.io, להריץ אותו על VPS אמיתי, ולוודא שהוא זמין ב-IP ציבורי.

מה תצטרכו: VPS (גודל מינימלי, 2GB RAM), כתובת IP, גישת SSH. אם אין לכם VPS, אפשר להריץ את התרגיל ב-Docker Desktop על הלפטופ עם docker run במקום deploy אמיתי.

מה תעשו:

  1. התחברו ל-VPS: ssh root@YOUR_VPS_IP.
  2. התקינו Docker:
    curl -fsSL https://get.docker.com | sh
    docker --version
    docker compose version
  3. צרו את התיקייה ו-clone: mkdir -p /app && cd /app && git clone https://github.com/YOUR_USER/YOUR_REPO.git .
  4. צרו .env: cp .env.example .env && nano .env (מלאו את הערכים האמיתיים).
  5. Login ל-ghcr.io: echo $CR_PAT | docker login ghcr.io -u YOUR_USER --password-stdin (אותו טוקן מתרגיל 2).
  6. משכו והריצו:
    docker compose pull
    docker compose up -d
    docker compose ps
  7. בדיקה: curl http://localhost (מה-VPS). אם אתם רואים HTML — האפליקציה חיה. עכשיו curl http://YOUR_VPS_IP מהלפטופ.
  8. ראו לוגים: docker compose logs -f --tail 100.

תוצאה צפויה: אפליקציה חיה בכתובת ה-IP של ה-VPS, נגישה מהדפדפן בכל מקום בעולם. בלי HTTPS, בלי domain, אבל חיה ורצה. אם לא עובד — docker compose logs יראה לכם למה.

Framework — מתי SSH ומתי Coolify

שתי שיטות deploy, שתי אסטרטגיות שונות. השאלה "איזו מתאימה לי" תלויה ב-4 קריטריונים:

המבחן הסופי: כמה פעמים בחודש אתם מתכוונים לעדכן? פעם-פעמיים → SSH מספיק. 5+ פעמים → Coolify משתלם.

Framework — סדר פעולות לאחר deploy שלא עובד

ה-container רץ על ה-VPS, אבל האפליקציה לא עונה. מאיפה מתחילים? סדר פעולות שמבוסס על שכיחות הבעיות ב-deploys אמיתיים:

  1. docker compose ps — האם ה-container ב-status "Up"? אם לא — חזרו לסעיף 1 (exits immediately).
  2. docker compose logs --tail 50 — מה האפליקציה אומרת? חפשו Error, FATAL, "cannot connect", "ECONNREFUSED".
  3. docker compose ps + docker inspect <id> | grep IPAddress — האם ה-container קיבל IP? אם לא — בעיית רשת, כנראה לא הגדרתם port mapping נכון.
  4. curl http://localhost:PORT מה-VPS — האם האפליקציה עונה מקומית? אם כן — הבעיה היא firewall של ה-VPS. אם לא — האפליקציה עצמה לא רצה.
  5. docker exec -it <id> sh — היכנסו לתוך ה-container. תריצו את הפקודה שמפעילה את האפליקציה ידנית. תראו את השגיאה האמיתית.
  6. docker scout cves myapp:v1 — לפעמים הבעיה היא באבטחה. נדיר, אבל קורה.

חוק אצבע: 80% מהבעיות נפתרות בצעד 1-3. רק 5% מגיעות לצעד 5-6.

Framework — מתי לעדכן את ה-base image

השאלה "האם לעדכן את node:20-slim לגרסה חדשה יותר" נשאלת בכל פעם ש-Docker Scout מראה חולשות. הכללים:

חוק אצבע: תעדכנו base image אחת לרבעון בערך, או מיד כשיש Critical CVE. לא לפני, לא אחרי.

Work Routine — שגרת ה-DevOps לאחר הקורס

הקורס הסתיים, אבל העבודה עם Docker לא. הנה השגרה השבועית שתשמור על האפליקציה שלכם בריאה גם חודשים אחרי ה-deploy הראשון:

יומי (5 דקות, בתחילת היום):

שבועי (30 דקות, יום ראשון):

חודשי (60 דקות, סוף חודש):

רבעוני (3 שעות, כל סוף-רבעון):

העיקרון: Docker זה לא "פרויקט חד-פעמי". זו תשתית חיה שדורשת תחזוקה שוטפת. השגרה הזו הופכת "תקלה ב-3 לפנות בוקר" ל"תיקון של 10 דקות ביום ראשון".

טעות נפוצה: להריץ container ב-VPS בלי firewall

ברירת המחדל של רוב ה-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 אחר.

טעות נפוצה: לא לתעד env vars — ואז לא לדעת איזה ערכים היו בפרודקשן

ה-VPS קרס. צריך לבנות אותו מחדש. יש לכם image ב-ghcr.io, יש לכם compose.yaml, אבל... איזה ערך היה ל-SECRET_KEY? מה היה DATABASE_URL? אם ה-env vars היו רק ב-VPS (בלי תיעוד), אתם נעולים החוצה. הפתרון: RUN-ANYWHERE.md הוא חובה, לא אופציה. רשמו שם את השמות של ה-env vars ואת המקור של הערכים (1Password, Bitwarden, AWS Secrets Manager). את הערכים עצמם אל תכתבו ב-git.

Just One Thing — אם תזכרו רק דבר אחד מהפרק הזה

אם תיקחו רק פעולה אחת מהפרק הזה — שתהיה זאת: כתבו היום את RUN-ANYWHERE.md עבור האפליקציה שלכם. עמוד אחד, התבנית מהסעיף הקפיטולי. רשמו: image tag, env vars בלי ערכים, volumes קריטיים, פקודת deploy, ו-3 פקודות debug. שמרו את זה ב-git. כי בעוד 3 חודשים, כשתרצו לעדכן את האפליקציה או לפרוס מחדש — אתם לא תזכרו. הקובץ הזה יזכור בשבילכם. זה ההבדל בין "פרויקט שעובד היום" לבין "פרויקט שעובד גם בעוד שנה, אחרי שהקשר שלכם עם ה-stack התקרר".

Check Yourself — 5 שאלות הבנה על הפרק
  1. שאלה: 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 או אופטימיזציה של האפליקציה.
  2. שאלה: מה ההבדל בין Docker Hub ל-ghcr.io עבור Vibe Coder שעובד לבד?
    תשובה: Docker Hub ציבורי דורש login אחרת מוגבל ל-100 pulls/6 שעות לכל IP (ובעייתי ב-CI/משרד משותף). ghcr.io חינמי עם חשבון GitHub, יושב ליד ה-repository, ובעל מכסה גבוהה בהרבה לשימוש סביר. ל-Vibe Coder שכבר על GitHub — ghcr.io היא ברירת המחדל.
  3. שאלה: למה צריך 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 עובד בשתי המכונות.
  4. שאלה: מה 3 הצעדים של deploy ידני ל-VPS דרך SSH?
    תשובה: אחרי ש-git clone הועתק ו-.env מולאו: (1) docker login ghcr.io, (2) docker compose pull, (3) docker compose up -d. הראשון מאמת את הזהות, השני מושך את ה-image החדש מ-registry, השלישי מריץ את ה-container החדש.
  5. שאלה: מה ההבדל בין deploy ידני (SSH) ל-Coolify? מתי כדאי להשתמש בכל אחד?
    תשובה: SSH = 3 פקודות ידניות אחרי כל push, מתאים לפרויקט יחיד עם deploys נדירים. Coolify = UI שמנהל CI/CD אוטומטי + HTTPS של Let's Encrypt + ניהול multi-app + גלגול לאחור (rollback), מתאים ל-2+ אפליקציות או deploys תכופים או לקוחות לא-טכניים. החסרון של Coolify: דורש VPS חזק יותר (2GB+ RAM).
סיכום הפרק — 8 לקחים שסוגרים את הקורס
  1. Container שיוצא מיד = תהליך ראשי סיים. Docker זקוק ל-PID 1 ב-foreground. אם הוא סיים — ה-container מת, בלי קשר לסיבה. docker ps -a + docker logs תמיד מראים למה.
  2. 5 פקודות debug מספיקות ל-95% מהבעיות: ps -a, logs, inspect, exec, ו-override של entrypoint. הסדר חשוב — התחילו מהזול.
  3. ghcr.io עדיף על Docker Hub ל-Vibe Coders. חינם עם חשבון GitHub, מכסה גבוהה, private repos ללא תשלום, native integration עם Actions.
  4. Docker Hub limits הם לפי IP, לא לפי משתמש. בבית לא בעיה. במשרד/CI — בעיה תוך שבוע. docker login או ghcr.io פותרים.
  5. exec format error = ארכיטקטורה לא תואמת. buildx עם multi-arch פותר את זה פעם אחת, ואז שתי המכונות מרוצות מאותו tag.
  6. deploy ידני = 3 פקודות: clone, pull, up. זה עובד בכל סביבה, בלי תלויות, ומתאים לפרויקטים קטנים.
  7. Coolify = Heroku חינמי על ה-VPS שלכם. CI/CD + HTTPS + multi-app + UI נוח. משתלם כשיש 2+ אפליקציות או deploys תכופים.
  8. RUN-ANYWHERE.md הוא ה-deliverable האמיתי של הקורס. עמוד אחד, תבנית קבועה, נשמר ב-git. בלי זה, הידע הולך לאיבוד תוך חודש.
Checklist — 12 סעיפים לפני שאתם סוגרים את הקורס
מה תפיקו בסוף הפרק (ובסוף הקורס)
מה הלאה — סוף הקורס, ולאן ממשיכים

זה היה הפרק האחרון של 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). אלה הקורסים הבאים שמתבססים על מה שלמדתם כאן. אבל לפני הכל — חגגו, ותתחילו לבנות. בהצלחה.