├── .env.example ├── .gitignore ├── README.md ├── docker-compose.yml ├── start.sh └── traefik.toml /.env.example: -------------------------------------------------------------------------------- 1 | # The network Traefik will run on 2 | # When you want to make Traefik detect a container, it needs to run on this network 3 | TRAEFIK_NETWORK=web 4 | 5 | # The primary domain 6 | # Traefik will use this as the default base domain for frontend rules 7 | # It's also the base domain the Traefik dashboard and the whoami website will run on 8 | PRIMARY_DOMAIN=example.com 9 | 10 | # The email to acquire Let's Encrypt certificates with 11 | LETS_ENCRYPT_EMAIL=info@example.com 12 | 13 | # The credentials to the Traefik dashboard at traefik.PRIMARY_DOMAIN 14 | # Get your user:pass string from htpasswd or http://www.htaccesstools.com/htpasswd-generator/ 15 | TRAEFIK_AUTH=user:pass 16 | 17 | # The domains that this webserver will run, including subdomains 18 | # This is needed to make inter-container routing work when using url's instead of container names 19 | # traefik.PRIMARY_DOMAIN is added automatically when running start.sh 20 | # Thanks to https://github.com/flexguse/traefik-inter-container-routing 21 | DOMAINS=(example.com admin.example.com example.dev) 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | acme.json 3 | docker-compose-network.yml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose Webserver 2 | 3 | My docker-compose configuration for a VPS I use that hosts some small web projects of mine. It contains a [Traefik 1.7](https://traefik.io/) container to serve as a reverse proxy with automatic Let's Encrypt SSL certificates and a [Watchtower](https://github.com/v2tec/watchtower) container to make sure my personal projects can update automatically. 4 | 5 | ## Usage 6 | 7 | 1. Clone this repository. 8 | 2. Copy `.env.example` to `.env` and modify the variables. 9 | 3. Run `./start.sh`. 10 | 11 | To stop the services, run `docker-compose down`. 12 | 13 | ### Traefik 14 | 15 | After starting the services, `traefik.example.com` and `traefik.example.com/ping` will be available (substitute example.com with the value of the `PRIMARY_DOMAIN` variable). The first one is guarded by the password specified in the `TRAEFIK_AUTH` variable and will redirect to the Traefik dashboard, while the second one doesn't have authentication and simply returns a HTTP 200 OK if everything's ok. 16 | 17 | Traefik is configured to check for containers on the network specified by the `TRAEFIK_NETWORK` variable, so containers that need to be accessible by Traefik will also need to run on this network. Besides that, containers are not exposed by default, so you'll need to set the `traefik.enable` label to `true` on every container that needs to be exposed by Traefik. 18 | 19 | When a new host rule is added, a Let's Encrypt SSL certificate is automatically acquired for it. The `LETS_ENCRYPT_EMAIL` variable specifies which email to set as the notification email. 20 | 21 | ### Watchtower 22 | 23 | Watchtower is set to check for updates every 15 minutes. It will only check for updates on containers running with the label `com.centurylinklabs.watchtower.enable` set to `true`. 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | traefik: 4 | image: traefik:1.7 5 | command: --docker.domain=${PRIMARY_DOMAIN} --acme.email=${LETS_ENCRYPT_EMAIL} 6 | ports: 7 | - "80:80" 8 | - "443:443" 9 | expose: 10 | - 8080 11 | volumes: 12 | - /var/run/docker.sock:/var/run/docker.sock 13 | - ./traefik.toml:/traefik.toml 14 | - ./acme.json:/acme.json 15 | labels: 16 | traefik.enable: true 17 | traefik.port: 8080 18 | traefik.backend: "traefik" 19 | traefik.frontend.rule: "Host:traefik.${PRIMARY_DOMAIN}" 20 | traefik.frontend.auth.basic.users: "${TRAEFIK_AUTH}" 21 | restart: unless-stopped 22 | watchtower: 23 | image: v2tec/watchtower 24 | command: --interval 900 --label-enable --cleanup 25 | volumes: 26 | - /var/run/docker.sock:/var/run/docker.sock 27 | labels: 28 | com.centurylinklabs.watchtower.enable: true 29 | restart: unless-stopped 30 | networks: 31 | traefik: 32 | name: ${TRAEFIK_NETWORK} 33 | external: true 34 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if .env file exists 4 | if [ -e .env ]; then 5 | source .env 6 | else 7 | echo "Please copy the .env.example file to .env and fill it with correct values." 8 | exit 1 9 | fi 10 | 11 | # Create acme.json file 12 | if [ ! -e acme.json ]; then 13 | touch acme.json 14 | chmod 600 acme.json 15 | fi 16 | 17 | # Create the docker-compose-network.yml file which enables inter-container routing 18 | network="version: '3.5' 19 | services: 20 | traefik: 21 | networks: 22 | traefik: 23 | aliases: 24 | " 25 | 26 | DOMAINS+=("traefik.${PRIMARY_DOMAIN}") 27 | for i in ${DOMAINS[@]}; do 28 | network+=" - ${i}" 29 | network+=$'\n' 30 | done 31 | 32 | echo -e "$network\c" > docker-compose-network.yml 33 | 34 | # Create the network if it does not exist yet 35 | docker network inspect $TRAEFIK_NETWORK &>/dev/null || docker network create $TRAEFIK_NETWORK 36 | 37 | # Start the containers 38 | docker-compose -f docker-compose.yml -f docker-compose-network.yml up -d 39 | -------------------------------------------------------------------------------- /traefik.toml: -------------------------------------------------------------------------------- 1 | defaultEntryPoints = ["http", "https"] 2 | 3 | logLevel = "INFO" 4 | 5 | [entryPoints] 6 | [entryPoints.http] 7 | address = ":80" 8 | [entryPoints.http.redirect] 9 | entryPoint = "https" 10 | [entryPoints.https] 11 | address = ":443" 12 | [entryPoints.https.tls] 13 | 14 | [acme] 15 | storage = "acme.json" 16 | entryPoint = "https" 17 | onDemand = false 18 | onHostRule = true 19 | [acme.httpChallenge] 20 | entryPoint = "http" 21 | 22 | [api] 23 | 24 | [ping] 25 | entryPoint = "https" 26 | 27 | [docker] 28 | endpoint = "unix:///var/run/docker.sock" 29 | watch = true 30 | exposedByDefault = false 31 | --------------------------------------------------------------------------------