{"id":28937538,"url":"https://github.com/heschmat/nginx-crash-course","last_synced_at":"2026-02-02T00:35:58.900Z","repository":{"id":294752395,"uuid":"986615993","full_name":"heschmat/nginx-crash-course","owner":"heschmat","description":"Simple example of NGINX reverse proxy and load balancing with multiple Node.js apps using Docker Compose. Demonstrates Docker networking, app-to-app communication, and troubleshooting.","archived":false,"fork":false,"pushed_at":"2025-06-09T21:08:26.000Z","size":418,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-22T21:36:18.101Z","etag":null,"topics":["docker","docker-compose","load-balancer","nginx","reverse-proxy"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heschmat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-19T21:59:35.000Z","updated_at":"2025-06-09T21:22:22.000Z","dependencies_parsed_at":"2025-05-22T12:15:33.219Z","dependency_job_id":null,"html_url":"https://github.com/heschmat/nginx-crash-course","commit_stats":null,"previous_names":["heschmat/nginx-crash-course"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/heschmat/nginx-crash-course","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heschmat%2Fnginx-crash-course","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heschmat%2Fnginx-crash-course/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heschmat%2Fnginx-crash-course/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heschmat%2Fnginx-crash-course/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heschmat","download_url":"https://codeload.github.com/heschmat/nginx-crash-course/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heschmat%2Fnginx-crash-course/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28997068,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T23:10:54.274Z","status":"ssl_error","status_checked_at":"2026-02-01T23:10:47.298Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","load-balancer","nginx","reverse-proxy"],"created_at":"2025-06-22T21:30:35.199Z","updated_at":"2026-02-02T00:35:58.895Z","avatar_url":"https://github.com/heschmat.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NGINX Reverse Proxy + Docker Compose + Multiple Apps\n\n## Project Overview\n\nIn this project, we built a simple multi-container setup using Docker Compose. We deployed 3 simple Node.js apps (`app1`, `app2`, `app3`) and an NGINX container configured as a reverse proxy and load balancer.\n\nWe also explored how networking works inside Docker, and how containers communicate with each other versus how they communicate with the host machine.\n\n---\n\n## Architecture\n\n* `app1`, `app2`, `app3`: Express servers exposing `port 3000` internally.\n* `nginx`: Acts as a reverse proxy and load balancer for the apps, exposed on host port `8080`.\n* All containers are connected to the same Docker Compose network: `nginx-crash-course_default`.\n\n```\nHOST\n |\n +--\u003e localhost:8080 --\u003e nginx --\u003e app1/app2/app3 (load balanced)\n |\n +--\u003e localhost:3000 --\u003e connection refused (port not mapped)\n\nDocker network \"nginx-crash-course_default\"\n |\n +--\u003e app1:3000\n +--\u003e app2:3000\n +--\u003e app3:3000\n +--\u003e nginx\n +--\u003e test container (alpine)\n```\n\n---\n\n## Key Learnings\n\n### 1. NGINX as Reverse Proxy + Load Balancer\n\n* We configured NGINX to load balance across `app1`, `app2`, `app3`.\n* We validated this setup by refreshing the browser multiple times and observing requests being distributed among the apps.\n* Each app logged which requests it received:\n\n```\n[App1] Received GET /\n[App2] Received GET /\n[App3] Received GET /\n```\n\n### 2. Docker Networking Model\n\n* Docker Compose creates an internal network: `nginx-crash-course_default`.\n* Containers can talk to each other by service name (`app1`, `app2`, `app3`).\n* Ports `3000` were only **exposed** to the Docker network, not **published** to the host.\n* That is why:\n\n  * `curl app1:3000` works inside a container on the same network.\n  * `curl localhost:3000` on the host fails (no port published).\n\n### 3. Testing Internally with Alpine Container\n\n* We ran an Alpine container inside the same network:\n\n```bash\ndocker run -it --rm --network nginx-crash-course_default alpine sh\napk add --no-cache curl\ncurl -v http://app1:3000/\n```\n\n* This validated that Docker networking and container DNS resolution were working correctly.\n\n### 4. Ports vs Expose\n\n* `expose: \"3000\"` in Compose allows containers on the same network to communicate.\n* To make `localhost:3000` on the host work, we would need `ports: \"3001:3000\"`, etc.\n* In our case, we intentionally only exposed `nginx` on host port `8080`, as NGINX is the public entry point.\n\n---\n\n## 📸 App Preview\n![App Homepage](public/images/homepage.png)\n\n## Summary\n\nWe successfully built and tested:\n\n* A multi-app architecture with NGINX reverse proxy.\n* Load balancing across services.\n* Deep understanding of Docker networking.\n* Practical testing techniques using a temporary container.\n\n---\n\n## Next Steps (Optional)\n\n* Add health checks for the apps in NGINX config.\n* Add round-robin weights or other load balancing strategies.\n* Add more apps or services.\n* Monitor traffic using NGINX access logs.\n\n---\n\n## Conclusion\n\nThis exercise helped us learn **how to build a reverse-proxy-based architecture** and **understand container-to-container vs host-to-container networking** in Docker.\n\nIt also gave us practical troubleshooting skills (test with temporary containers, validate DNS resolution, understand `expose` vs `ports`).\n\nKeep learning! 🚀\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheschmat%2Fnginx-crash-course","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheschmat%2Fnginx-crash-course","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheschmat%2Fnginx-crash-course/lists"}