{"id":27153421,"url":"https://github.com/tankengine-ish/traefik_multi_app_hosting","last_synced_at":"2026-01-21T12:34:55.168Z","repository":{"id":285394927,"uuid":"957983734","full_name":"TankEngine-ish/traefik_multi_app_hosting","owner":"TankEngine-ish","description":"A Traefik configuration for hosting multiple web applications with automatic HTTPS, rate limiting, and security headers [personal project]","archived":false,"fork":false,"pushed_at":"2025-04-01T11:31:13.000Z","size":414,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-08T16:16:51.497Z","etag":null,"topics":["duckdns","letsencrypt","traefik"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TankEngine-ish.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2025-03-31T13:03:47.000Z","updated_at":"2025-04-01T11:31:17.000Z","dependencies_parsed_at":"2025-03-31T14:29:03.709Z","dependency_job_id":"c284dac0-2256-4df9-ae0c-a0c10d8e2cdf","html_url":"https://github.com/TankEngine-ish/traefik_multi_app_hosting","commit_stats":null,"previous_names":["tankengine-ish/traefik_multi_app_hosting"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TankEngine-ish/traefik_multi_app_hosting","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TankEngine-ish%2Ftraefik_multi_app_hosting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TankEngine-ish%2Ftraefik_multi_app_hosting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TankEngine-ish%2Ftraefik_multi_app_hosting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TankEngine-ish%2Ftraefik_multi_app_hosting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TankEngine-ish","download_url":"https://codeload.github.com/TankEngine-ish/traefik_multi_app_hosting/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TankEngine-ish%2Ftraefik_multi_app_hosting/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28632841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["duckdns","letsencrypt","traefik"],"created_at":"2025-04-08T16:16:54.617Z","updated_at":"2026-01-21T12:34:55.150Z","avatar_url":"https://github.com/TankEngine-ish.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Overview\n\nI decided to set up a multi-application container environment using Traefik as a reverse proxy in order to have some alternative to AWS.\nI encountered several challenges along the way and this document chronicles my process: from initial configuration attempts to the final working solution.\n\n## Initial Setup: The Applications\n\nI began with two distinct applications that needed to coexist and be easily accessible:\n\n- **Dead Space Exploration App - A Node.js/Express backend with React frontend and MongoDB database** - A simple Space App that uses some outdated SpaceX Api from github.\n\n- **Property Management System - A Go backend with Next.js frontend and PostgreSQL database** - A Property Management App that tracks the financial reserve for property repairs.\n\nMy goal was to use Traefik as a reverse proxy to route traffic to these applications based on domain names:\n\n* (https://majortomtogroundcontrol.duckdns.org/) \n* (https://propertyapp.duckdns.org/) \n\n### Challenge 1: Choosing the Right Reverse Proxy\nInitially, I attempted to use Envoy Proxy for this task. While it offers some nice features for microservice architectures, I quickly discovered that I can't deal with verbose YAML configuration and lack of automatic service discovery so soon after setting up my property app infrastructure - https://github.com/TankEngine-ish/property_management_system_infrastructure\n\nAfter careful consideration, I pivoted to Traefik, which offers:\n\nAutomatic service discovery through Docker labels and built-in dashboard for monitoring and troubleshooting.\nLook at how nice and clean it is: \n![alt text](\u003cassets/Screenshot from 2025-03-31 16-31-27.png\u003e)\n\n\n### Challenge 2: Hardcoded API URL in the Next.js Application\nPerhaps the most annoying issue was me finding out that API requests from the Property Management frontend were consistently attempting to reach the stupidly hardcoded AWS IP address.\nThis reminded me that when Next.js applications have environment variables with the NEXT_PUBLIC_ prefix are embedded in the JavaScript bundle at build time, not injected at runtime. This also happened while I was setting up my Helm charts. Despite commenting out this line and providing a new environment variable in docker-compose.yml, the hardcoded value persisted because it was already compiled into the JavaScript bundle.\n\nUltimately, I modified the Dockerfile to accept a build argument:\n\n```\nARG NEXT_PUBLIC_API_URL=http://property.localhost\nENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}\n```\n\nAnd passed this argument during the build process:\n\n```\nyamlCopynextapp:\n  build:\n    context: ../property_management_system_full_stack_app/frontend\n    dockerfile: next.dockerfile\n    args:\n      - NEXT_PUBLIC_API_URL=http://property.localhost\n```\n\nThis approach ensured that the correct URL was embedded in the JavaScript bundle during the build process. **This was before I set-up the DNS routes.**\n\n### Challenge 5: Cross-Origin Resource Sharing (CORS)\n\nAfter resolving the hardcoded URL issue, I encountered some CORS errors such as:\n```\nCopyAccess to XMLHttpRequest at 'http://property.localhost/api/repair/users' from origin 'http://172.18.0.5:3000' has been blocked by CORS policy\n```\n**Again, this issue happened before I set-up the DNS.**\nThese errors occurred because the frontend was being accessed at its container's IP address and port rather than through Traefik at property.localhost. \nI addressed this by:\n\n* Adding appropriate CORS headers to the Go backend\n* Configured Traefik to add CORS headers through middleware\n\n```\nyamlCopy- \"traefik.http.middlewares.cors.headers.accessControlAllowOriginList=*\"\n- \"traefik.http.middlewares.cors.headers.accessControlAllowMethods=GET,POST,PUT,DELETE,OPTIONS\"\n- \"traefik.http.middlewares.cors.headers.accessControlAllowHeaders=Content-Type,Authorization\"\n- \"traefik.http.routers.goapp.middlewares=cors@docker\"\n```\n\n![alt text](\u003cassets/Screenshot from 2025-03-31 16-32-15.png\u003e)\n\n\n## A Health Check Script in JS\n\nWhat this monitoring.js script in the repository does is it:\n\n- Checks the health of my Traefik dashboard and applications by making HTTP requests\n- Verifies SSL certificate expiration dates\n- Monitors response times and alerts if they exceed thresholds\n- Checks the status of my Docker containers\n- Generates alerts for any issues detected\n- Displays colorized output in the console like this: \n\n![alt text](\u003cassets/Screenshot from 2025-04-01 14-09-54.png\u003e)\n\nI gotta be honest, the main reason I added this script is because I just didn't like not having a Language used star under my github's repository name. Now I have Javascript which is better than nothing.\n\nP.S. Make sure you have Node.js installed on your system and then you run the script with:\n\n```\nnode monitoring.js\n```\n\n## Final thoughts\nWith this configuration, all services ran harmoniously, with Traefik correctly routing requests to the appropriate applications based on the domain name. Both the Dead Space App and Property Management System were accessible through their respective domains.\n\n![alt text](\u003cassets/Screenshot from 2025-03-31 16-32-22.png\u003e)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftankengine-ish%2Ftraefik_multi_app_hosting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftankengine-ish%2Ftraefik_multi_app_hosting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftankengine-ish%2Ftraefik_multi_app_hosting/lists"}