{"id":18445662,"url":"https://github.com/voronenko/traefik2-compose-template","last_synced_at":"2025-04-08T00:31:40.417Z","repository":{"id":92366562,"uuid":"244367970","full_name":"Voronenko/traefik2-compose-template","owner":"Voronenko","description":"Check out fiks.im - unobtrusive local development with traefik2, docker and green sealed wildcard certs with  letsencrypt. Introducing ultimate local development environment for docker projects with traefik2 and letsencrypt","archived":false,"fork":false,"pushed_at":"2025-03-18T14:46:39.000Z","size":1187,"stargazers_count":24,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T15:45:33.797Z","etag":null,"topics":["acme-sh","docker","letsencrypt","local-development","lvh","traefik2"],"latest_commit_sha":null,"homepage":"https://www.fiks.im","language":"Shell","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/Voronenko.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":"2020-03-02T12:40:12.000Z","updated_at":"2025-03-18T14:46:43.000Z","dependencies_parsed_at":"2025-03-01T11:26:28.755Z","dependency_job_id":"827d1b1c-e72d-4770-afe3-9548229570ba","html_url":"https://github.com/Voronenko/traefik2-compose-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voronenko%2Ftraefik2-compose-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voronenko%2Ftraefik2-compose-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voronenko%2Ftraefik2-compose-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voronenko%2Ftraefik2-compose-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Voronenko","download_url":"https://codeload.github.com/Voronenko/traefik2-compose-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247755340,"owners_count":20990616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["acme-sh","docker","letsencrypt","local-development","lvh","traefik2"],"created_at":"2024-11-06T07:06:44.062Z","updated_at":"2025-04-08T00:31:38.920Z","avatar_url":"https://github.com/Voronenko.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unobtrusive local development with traefik2, docker and letsencrypt\n\nWhen it comes to developing sites that use subdomains, this can be a challenging or boring task to set up in development because localhost isn’t a fully qualified domain name. You can’t just goto example.localhost. You either constantly edit /etc/hosts or setting up\nsome dnsmasq in your local network.\n\nSo far most often used solutions by myself were http://lvh.me - a free service that resolves itself along with all subdomains to localhost,\nand similar to it service nip.io which allows to code ip address to resolve into domain name 192-168-1-250.nip.io, and thus allowing to\ngenerate letsencrypt certificate to mentioned domain.\n\nThis played good for a half of the decade, but as more and more of mine clients were moving to pull request reviews and choosing docker or\nkubernetes as a platform in parallel, I found myself more and more limited with classic solutions above.\n\nWhat I wanted last year - is reproducible scenario to implement own, branded local development environment that I can easily reproduce\non a remote server to implement pull request reviews scenarios, supporting some reasonable perks like green seal certificates, some troubleshooting capabilities and so on.\n\nLet's take a look on a approach I am using now for clients using dockerized applications (either local or swarm)\n\n## Choosing components for the solution\n\nCooking components:\n\na) docker - classic or in swarm mode. (On my work notebook I have docker in swarm mode, it does not stop it from be used as a standalone docker environment too)\n\nb) traefik2 , and it's official docker image. Traefik is an open-source edge router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them. Kind of interest for me are two backends supported - docker and kubernetes.\n\nc) Development subdomain. For nicely working solution we need to dedicate some subdomain for development, for example: `*.lvh.voronenko.net` - resolving to localhost, or `*.preview.project.net` - some wildcard domain pointing to your staging server. If you want branding neutral naming - you can use '*.fiks.im' as well.\n\nd) Wildcard certificate from letsencrypt to organize green seal certificate. Although on remote box traefik might take care on obtaining green seal certificate for each exposed fqdn for you, if you have possibility to pregenerate wildcard certificate - why not?\n\nFor domain resolving to localhost, like mentioned `*.lvh.voronenko.net` - you need to take care of certificate on your own.\nNew! If you want pre-generated grean seal certificates for local development - feel free to use `*.fiks.im` domain that resolves to localhost. Pregenerated certificates available at \u003chttps://github.com/Voronenko/fiks.im\u003e, check for details\n\nFor generating letsencrypt certificates my current tool of choice - is acme.sh - shell zero dependency tool. It supports number of dns providers, and generating wildcard certificate might be as simple as running short shell command.\n\n```sh\n\nacme.sh --issue --dns dns_gd -d lvh.voronenko.net -d \"*.lvh.voronenko.net\"\n```\n\nNote that tool also takes care on prolonging certificate when necessary.\nInstalling certificates into necessary folder also is as simple as executing shell script\n\n```sh\n~/.acme.sh/acme.sh --install-cert -d \"${DOMAIN_NAME}\" \\\n        --cert-file $TARGET/cert.pem \\\n        --key-file  $TARGET/privkey.pem \\\n        --fullchain-file $TARGET/fullchain.pem\n```\n\nIn worse case scenario (if you haven't instructed acme.sh with a command what to do on certificate prolongation) - you would need to take care on copying new set of certs once in 93 days.\n\ne) Some tool you can inspect what happens with docker. There are many good console tools, but if you like one with web ui - portainer is definitely one of the best.\n\nNow, when we have discussed components, let's combine them together:\n\n## Local setup - instance wide traefik setup for local development\n\n![alt](https://github.com/Voronenko/traefik2-compose-template/raw/master/docs/structure.png)\n\nUPD: Now with branding neural domain - fiks.im (guaranteed until 2033) - pre-generated green seal certificates available. Check \u003chttps://github.com/Voronenko/fiks.im\u003e for details.\n\nWe need:\n\n`traefik_certs` folder, where we would store pregenerated wildcard certificate (you might have more than one) also here traefik will store information about own certificates.\n\n`traefik_conf` folder, where we would store parts of the traefik configuration we want to exclude from docker-compose.\n\nBy default, `certificates.toml` tells traefik that we have one pregenerated certificate, which can be found under specified path in certs folder.\n\n```toml\n[[tls.certificates]] #first certificate\n   certFile = \"/certs/fullchain.pem\"\n   keyFile = \"/certs/privkey.pem\"\n\n#[[tls.certificates]] #second certificate\n#   certFile = \"/path/to/other.cert\"\n#   keyFile = \"/path/to/other.key\"\n```\n\nControlling `Makefile` which helps you to create shared public network for docker to be used by traefik to look for exposed services, and up/down routines\n\n```makefile\n\ncreate-traefik-network-once:\n        docker network create --attachable traefik-public\nup:\n        docker-compose up -d\ndown:\n        docker-compose down\n```\n\nand heart of the construction: main traefik docker-compose file, in order to make story shorter I will comment most important parts of the configuration\n\n```yaml\n\nversion: '3.4'\nservices:\n  traefik:\n    image: traefik:v2.10.4\n# on my notebook traefik serves on default http https ports\n# this allows natural urls like https://app.fiks.im/\n    ports:\n      - 80:80\n      - 443:443\n# setup can be easily transformed into swarm deploy\n    deploy:\n      replicas: 1\n      placement:\n        constraints:\n          - node.role == manager\n        preferences:\n          - spread: node.id\n      labels: []\n# /certs and /conf are dedicated known directories in\n# official traefik image.\n# in order to allow traefik to expose docker backend we are\n# mapping docker socket inside.\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n      - ./traefik_certs:/certs\n      - ./traefik_conf:/conf\n    restart: always\n# if traefik supposed to serve real swarm cluster, peram should be specified\n#       --docker.swarmmode\n    command:\n      - \"--api.insecure=true\"\n      - \"--providers.docker=true\"\n      - \"--providers.docker.watch=true\"\n      - \"--providers.docker.exposedbydefault=false\"\n      - \"--providers.file.directory=/conf/\"\n      - \"--providers.file.watch=true\"\n      - \"--entrypoints.web.address=:80\"\n      - \"--entrypoints.websecure.address=:443\"\n      - \"--log.level=DEBUG\"\n      - \"--accessLog\"\n      - \"--api\"\n      - \"--metrics\"\n      - \"--metrics.prometheus\"\n      - \"--providers.docker.network=traefik-public\"\n    networks:\n      - default\n      - traefik-public\n# traefik configuration via labels\n# expose traefik dashboard under address https://traefik.fiks.im\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.traefik.rule=Host(`traefik.fiks.im`)\"\n      - \"traefik.http.routers.traefik.entrypoints=websecure\"\n      - \"traefik.http.routers.traefik.tls.certresolver=letsencryptresolver\"\n      - \"traefik.http.routers.traefik.service=api@internal\"\n      - \"traefik.http.routers.traefik.tls=true\"\n# docker management UI to be exposed under\n# https://docker.fiks.im\n  portainer:\n    image: portainer/portainer\n    restart: always\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n      - portainer_data:/data\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.portainer.rule=Host(`docker.fiks.im`)\"\n      - \"traefik.http.routers.portainer.entrypoints=web\"\n      - \"traefik.http.routers.portainer-secure.rule=Host(`docker.fiks.im`)\"\n      - \"traefik.http.routers.portainer-secure.entrypoints=websecure\"\n      - \"traefik.http.routers.portainer-secure.tls=true\"\nvolumes:\n  portainer_data:\nnetworks:\n  traefik-public:\n    external: true\n```\n\nOn a that moment, if you start the setup, i.e. `docker-compose up -d`, you already will get:\n\na) traefikUI at https://traefik.fiks.im/ - not to much to change, but a nice bird eye view\non currently detected services. Note, that page is served on a nice grean seal certificate you configured.\n\n![alt](https://github.com/Voronenko/traefik2-compose-template/raw/master/docs/traefikui.png)\n\nb) PortainerUI at https://docker.fiks.im/\n![alt](https://github.com/Voronenko/traefik2-compose-template/raw/master/docs/portainer.png) which provides detailed insides\nin docker running on your machine, containers, services and et cetera.\n\n\nNote that traefik docker-compose is fully independent component of your system without direct relation to any project you are currently worked on. This means that it can be constantly running on your host, like any other web-server.\n\nAt a moment when you need to expose some docker project you are currently working on to a traefik, you would need\n\na) add service exposed outside to the `traefik-public` network.\n\nb) specify discovery rules, like fqdn for the service - \"Host(`whoami.fiks.im`)\"\n\nc) provide hints to traefik for any additional configuration,\nlike redirections, authorization/password protection etc.\n\nOnce you launch such docker-compose in your project folder, it will immediately get exposed with traefik. Moreover - all your teammates working on project have exactly same experience, but on their own notebooks.\n\n\n```yaml\n\n  whoami:\n    image: \"containous/whoami\"\n    container_name: \"simple-service\"\n    networks:\n      - app\n      - traefik-public\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.whoami.rule=Host(`whoami.fiks.im`)\"\n      - \"traefik.http.routers.whoami.entrypoints=web\"\n#      - \"traefik.http.middlewares.traefik-auth.basicauth.users=USER:PASSWORD\"\n#      - \"traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https\"\n#      - \"traefik.http.routers.traefik.middlewares=traefik-https-redirect\"\n      - \"traefik.http.routers.traefik-secure.entrypoints=websecure\"\n      - \"traefik.http.routers.traefik-secure.rule=Host(`whoami.fiks.im`)\"\n#      - \"traefik.http.routers.traefik-secure.middlewares=traefik-auth\"\n      - \"traefik.http.routers.traefik-secure.tls=true\"\nnetworks:\n  traefik-public:\n    external: true\n\n```\n\nYour service(s) get exposed on a dedicated names, serving on a green seal certificates - almost identical copy of your production environment.\n\n![alt](https://github.com/Voronenko/traefik2-compose-template/raw/master/docs/whoami.png)\n\n\n## Installing setup on a public server\n\nInstance wide traefik setup for remote development (branch deploys, etc) is done in a similar way, but you would need to protect better your intellectual property (saying you do not want anyone on a web to preview your branch deploys) as well as protect portainer and traefik dashboards with credentials or even fully remove them from public access.\n\n![alt](https://github.com/Voronenko/traefik2-compose-template/raw/master/docs/middleware.png)\n\nTraefik has multiple middlewares to choose from  https://docs.traefik.io/middlewares/overview/\n\nMost reasonable middlewares to consider would be\n\nBasicAuth  https://docs.traefik.io/middlewares/basicauth/\n\nDigestAuth https://docs.traefik.io/middlewares/digestauth/\n\nIPWhiteList https://docs.traefik.io/middlewares/ipwhitelist/\n\nRateLimit   https://docs.traefik.io/middlewares/ratelimit/\n\nalso for a public server traefik will take care on a generating green seal certificates for you, if you haven't provided pregenerated wildcard certificate.\n\nAlso changes to the `local_server` setup would be activating letsencrypt certificates provider\nWe introduce new mapped letsencrypt folder to store automatically retrieved certificates.\n\n```yaml\n\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n      - ./traefik_certs:/certs\n      - ./traefik_conf:/conf\n      - ./letsencrypt:/letsencrypt\n```\n\nand we additionally have to activate letsencrypt acme provider in traefik\n\n```yaml\nlabels:\n      - \"--certificatesResolvers.letsencrypt.acme.email=\u003cLETSENCRYPT_MAIL_ADDRESS\u003e\"\n      - \"--certificatesResolvers.letsencrypt.acme.tlsChallenge=true\"\n      - \"--certificatesResolvers.letsencrypt.acme.httpChallenge=true\"\n      - \"--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web\"\n      - \"--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\"\n```\n\nHowever, I recommend at least for host serving as branch deploys preview, pregenerate wild card certificate.\nIt is quite useless to generate new certificate per every new branch deployed (branchname.preview.voronenko.net)\n\nFor services exposed through traefik, requiring automatic certificate from letsencrypt, you would need to instruct traefik to use letsencrypt for that service.\n\n```yaml\nlabels:\n      - traefik.http.routers.whoami.tls.certresolver=letsencrypt\n```\n\nFull example for the service exposed:\n\n```yaml\n\n  whoami:\n    image: \"containous/whoami\"\n    container_name: \"simple-service\"\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.whoami.rule=Host(`whoami.preview.voronenko.net`)\"\n      - \"traefik.http.routers.whoami.entrypoints=web\"\n#      - \"traefik.http.routers.traefik-secure.middlewares=traefik-auth\"\n#      - \"traefik.http.middlewares.traefik-auth.basicauth.users=USER:PASSWORD\"\n#      - \"traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https\"\n#      - \"traefik.http.routers.traefik.middlewares=traefik-https-redirect\"\n      - \"traefik.http.routers.traefik-secure.entrypoints=websecure\"\n      - \"traefik.http.routers.traefik-secure.rule=Host(`whoami.preview.voronenko.net`)\"\n      - \"traefik.http.routers.traefik-secure.tls=true\"\n      - traefik.http.middlewares.whoami.compress=true\n      - traefik.http.routers.whoami.tls.certresolver=letsencrypt\n     networks:\n       traefik-public\n```\n\n\n## Summary\n\nWith described approach you are able to provide unobtrusive local development with traefik2, docker and letsencrypt individually as well as for all your teammates. Startup owners are able to enforce \"branded\" development environment, like `app.lvh.mystartup.domain`\n\nSolution is universal, and works nicely with multiple projects, including standalone tools distributed as docker container.\n\nYou can easily able to extend approach to public server, implementing \"preview server\" for the same components. Traefik and docker allow you possibility also to introduce pull request previews in a reasonable time (in a day)\n\nMentioned in article examples can be tried at https://github.com/Voronenko/traefik2-compose-template\n\n`local_server` is the example of the development environment on your localhost, i.e. https://someapp.fiks.im/\n\n`public_server` is the example of the environment environment that can be deployed to the public server.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoronenko%2Ftraefik2-compose-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvoronenko%2Ftraefik2-compose-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoronenko%2Ftraefik2-compose-template/lists"}