├── README.md ├── atlassian └── docker-compose.yml ├── gitlab └── docker-compose.yml ├── nexus └── docker-compose.yml ├── portainer └── docker-compose.yml ├── rocketchat └── docker-compose.yml └── traefik2 ├── acme.json ├── docker-compose.yml ├── provider_file.toml └── traefik.toml /README.md: -------------------------------------------------------------------------------- 1 | 2 | # My Docker Development Stack (traefik, gitlab, Jira, Confluence, Crowd, Rocketchat & Portainer) 3 | 4 | Some days ago traefik released it's brand new Version 2 (RC1) including http and TCP routing (including SSH) - YAY! So i've rebuilt our docker development stack to consolidate all needed services from different machines on a new all-in-one docker server. 5 | 6 | First of all: The documentation of traefik V2 is huge and detailed, but it's impossible to find any good tutorials or copy-and-paste examples to get things fast up and running. However, after many hours of reading the forums, searching github issues and drinking wine, i had all containers in production. 7 | 8 | If you have any questions - just [open an issue](https://github.com/realtarget/traefik2-docker-stack/issues) :-) 9 | 10 | ## traefik v2 Proxy 11 | 12 | My configuration includes the following files: 13 | * docker-compose.yml *for the docker container* 14 | * traefik.toml *for general traefik configuration* 15 | * provider_file.toml *to define a global accessible http to https redirect middleware* 16 | * acme.json *for letsencrypt* 17 | 18 | #### Lets Encrypt integration 19 | 20 | It's important to 21 | ``` 22 | sudo touch /var/acme.json 23 | sudo chmod 600 /var/acme.json 24 | ``` 25 | for security purposes. The file can be left empty and will be automatically filled with the letsencrypt responses. 26 | 27 | #### http-to-https redirect middleware 28 | 29 | I use two entry points for each webservice. One for (unencrypted) http traffic and one for https. So we need to define a middleware in the docker labels for unencrypted port 80 access. 30 | 31 | ``` 32 | # Entry point for http 33 | - traefik.http.routers.traefik.entrypoints=web 34 | # Listen domain 35 | - traefik.http.routers.traefik.rule=Host(`traefik.domain.com`) 36 | # Use a middleware named "redirect" to forward the request to https (defined in provider_file.toml) 37 | - traefik.http.routers.traefik.middlewares=redirect@file 38 | ``` 39 | 40 | Due to the fact that i want to reuse the middleware i've created a separate provider file which contains the configuration for the new scheme. 41 | 42 | #### Example label for secure entrypoint to redirect the traefik dashboard 43 | 44 | The docker-compose.yml includes necessary rules to access the traefik dashboard via traefic via https, 45 | 46 | ``` 47 | # secure entry point (port 443) 48 | - traefik.http.routers.traefik_secure.entrypoints=web-secure 49 | # Listen domain 50 | - traefik.http.routers.traefik_secure.rule=Host(`traefik.domain.com`) 51 | # Letsentrypt 52 | - traefik.http.routers.traefik_secure.tls.certresolver=letsencrypt 53 | # Port for traefik dashboard 54 | - traefik.http.services.traefik.loadbalancer.server.port=8080 55 | ``` 56 | Naming convention: I usually use the name of the app for the routers definition eg. traefik and add _secure for the secure entry point. Router names can only used once for all running docker services. 57 | 58 | ## Gitlab (https + SSH via traefik) 59 | 60 | The main reason for switching to traefik v2 was that it supports hostname based tcp routing. All versions below only worked for web (http + https). Out old (dedicated and undockerized) gitlab server used port 22 for ssh access. With the new possibilities of traefic v2 we are able to run gitlab in a docker environment which is easier to maintain. 61 | 62 | #### docker-compose.yml 63 | 64 | First of all, define the ssh port in the gitlab environment variables so that all links in the "clone repository" section work: 65 | 66 | # This refers to the ssh port Traaefik has for the ssh entry point 67 | gitlab_rails['gitlab_shell_ssh_port'] = 2222 68 | 69 | And the traefik labels: 70 | 71 | # define hostname for the gitlab-ssh router 72 | - traefik.tcp.routers.gitlab-ssh.rule=HostSNI(`gitlab.domain.com`) 73 | # define the ssh entry point 74 | - traefik.tcp.routers.gitlab-ssh.entrypoints=ssh 75 | # define service to use 76 | - traefik.tcp.routers.gitlab-ssh.service=gitlab-ssh-svc 77 | # define backend port to use, this is the port Gitlab ssh listens on 78 | - traefik.tcp.services.gitlab-ssh-svc.loadbalancer.server.port=22 79 | 80 | #### treafik.toml 81 | 82 | If you want to access ssh on port 2222 you also need to add this as a new entry point in the traefik.toml: 83 | 84 | [entryPoints.ssh] 85 | address = ":2222" 86 | 87 | #### Port 22 vs. 2222 88 | If your servers ssh daemon listens on another port than 22 it's possible to use 22 for gitlab. Just change the port number to a port of your choice. 89 | 90 | ## Rocketchat 91 | Configs are self-explaining if you take a look at the traefik and gitlab config. 92 | 93 | ## Atlassian Confluence, Jira Software + Crowd 94 | Configs are self-explaining if you take a look at the traefik and gitlab config. 95 | 96 | ## Portainer 97 | Configs are self-explaining if you take a look at the traefik and gitlab config. 98 | 99 | ## Nexus 100 | Configs are self-explaining if you take a look at the traefik and gitlab config. 101 | 102 | ## Server Specs 103 | 104 | ### Hardware 105 | * Intel Haswell i5-4590 (quad-core, up to 4x 3,7 GHz) 106 | * 32 GB DDR3 RAM 107 | * 2x 500GB SSD 108 | * 1 Gbit/s Uplink 109 | 110 | ### Software 111 | * Ubuntu 18.04 LTS 112 | * Docker 18.09.7 113 | * docker-compose version 1.25.0-rc2 114 | 115 | ### Useful links 116 | - [Traefik (Github)](https://github.com/containous/traefik) 117 | - [Traefik v2 Documentation](https://docs.traefik.io/v2.0/) 118 | - [Traefik Forums v2](https://community.containo.us/c/traefik/traefik-v2) 119 | 120 | -------------------------------------------------------------------------------- /atlassian/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | 4 | db: 5 | restart: always 6 | container_name: db 7 | image: postgres 8 | restart: always 9 | environment: 10 | POSTGRES_PASSWORD: PG_PASSWORD 11 | command: ["-c", "shared_buffers=256MB", "-c", "max_connections=200"] 12 | volumes: 13 | - atlassian_postgres:/var/lib/postgresql/data 14 | 15 | adminer: 16 | image: adminer 17 | container_name: db_adminer 18 | restart: always 19 | expose: 20 | - "8080" 21 | networks: 22 | - traefik-proxy 23 | - default 24 | labels: 25 | - traefik.enable=true 26 | - traefik.http.routers.dbadmin_insecure.entrypoints=web 27 | - traefik.http.routers.dbadmin_insecure.rule=Host(`dbadmin.domain.com`) 28 | - traefik.http.routers.dbadmin_insecure.middlewares=redirect@file 29 | 30 | - traefik.http.routers.dbadmin.entrypoints=web-secure 31 | - traefik.http.routers.dbadmin.rule=Host(`dbadmin.domain.com`) 32 | - traefik.http.routers.dbadmin.tls.certresolver=letsencrypt 33 | - traefik.http.services.dbadmin.loadbalancer.server.port=8080 34 | - traefik.docker.network=traefik-proxy 35 | 36 | crowd: 37 | image: teamatldocker/crowd 38 | container_name: crowd 39 | restart: always 40 | networks: 41 | - traefik-proxy 42 | - default 43 | volumes: 44 | - crowd_data:/var/atlassian/crowd 45 | links: 46 | - db 47 | expose: 48 | - "8095" 49 | environment: 50 | - CROWD_PROXY_NAME=crowd.domain.com 51 | - CROWD_PROXY_PORT=443 52 | - CROWD_PROXY_SCHEME=https 53 | - CROWD_URL=http://localhost:8095 54 | - LOGIN_BASE_URL=http://localhost:8095 55 | - SPLASH_CONTEXT= 56 | - CROWD_CONTEXT=ROOT 57 | - CROWDID_CONTEXT= 58 | - OPENID_CLIENT_CONTEXT= 59 | - DEMO_CONTEXT= 60 | labels: 61 | - "com.blacklabelops.description=Atlassian Crowd" 62 | - "com.blacklabelops.service=crowd" 63 | - traefik.enable=true 64 | - traefik.http.routers.crowd_insecure.entrypoints=web 65 | - traefik.http.routers.crowd_insecure.rule=Host(`crowd.domain.com`) 66 | - traefik.http.routers.crowd_insecure.middlewares=redirect@file 67 | 68 | - traefik.http.routers.crowd.entrypoints=web-secure 69 | - traefik.http.routers.crowd.rule=Host(`crowd.domain.com`) 70 | - traefik.http.routers.crowd.tls.certresolver=letsencrypt 71 | - traefik.http.services.crowd.loadbalancer.server.port=8095 72 | - traefik.docker.network=traefik-proxy 73 | 74 | jira-software: 75 | restart: always 76 | image: atlassian/jira-software 77 | container_name: jira-software 78 | environment: 79 | - ATL_PROXY_NAME=jira.domain.com 80 | - ATL_PROXY_PORT=443 81 | - ATL_TOMCAT_SCHEME=https 82 | expose: 83 | - "8080" 84 | links: 85 | - db 86 | volumes: 87 | - jira_software_data:/var/atlassian/application-data/jira 88 | - /etc/localtime:/etc/localtime:ro 89 | networks: 90 | - traefik-proxy 91 | - default 92 | labels: 93 | - traefik.enable=true 94 | - traefik.http.routers.jira_insecure.entrypoints=web 95 | - traefik.http.routers.jira_insecure.rule=Host(`jira.domain.com`) 96 | - traefik.http.routers.jira_insecure.middlewares=redirect@file 97 | 98 | - traefik.http.routers.jira.entrypoints=web-secure 99 | - traefik.http.routers.jira.rule=Host(`jira.domain.com`) 100 | - traefik.http.routers.jira.tls.certresolver=letsencrypt 101 | - traefik.http.services.jira.loadbalancer.server.port=8080 102 | - traefik.docker.network=traefik-proxy 103 | 104 | confluence: 105 | restart: always 106 | image: atlassian/confluence-server:latest 107 | container_name: confluence 108 | environment: 109 | - CATALINA_CONNECTOR_PROXYNAME=confluence.domain.com 110 | - CATALINA_CONNECTOR_PROXYPORT=443 111 | - CATALINA_CONNECTOR_SCHEME=https 112 | expose: 113 | - "8090" 114 | links: 115 | - db 116 | volumes: 117 | - confluence_data:/var/atlassian/application-data/confluence 118 | - /etc/localtime:/etc/localtime:ro 119 | networks: 120 | - traefik-proxy 121 | - default 122 | labels: 123 | - traefik.enable=true 124 | - traefik.http.routers.confluence_insecure.entrypoints=web 125 | - traefik.http.routers.confluence_insecure.rule=Host(`confluence.domain.com`) 126 | - traefik.http.routers.confluence_insecure.middlewares=redirect@file 127 | 128 | - traefik.http.routers.confluence.entrypoints=web-secure 129 | - traefik.http.routers.confluence.rule=Host(`confluence.domain.com`) 130 | - traefik.http.routers.confluence.tls.certresolver=letsencrypt 131 | - traefik.http.services.confluence.loadbalancer.server.port=8090 132 | - traefik.docker.network=traefik-proxy 133 | 134 | networks: 135 | traefik-proxy: 136 | external: true 137 | 138 | volumes: 139 | atlassian_postgres: 140 | confluence_data: 141 | crowd_data: 142 | jira_software_data: 143 | 144 | -------------------------------------------------------------------------------- /gitlab/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | # The GitLab container itself 5 | gitlab: 6 | image: 'gitlab/gitlab-ce:latest' 7 | restart: always 8 | hostname: 'gitlab.mydomain.com' 9 | environment: 10 | GITLAB_OMNIBUS_CONFIG: | 11 | external_url 'https://gitlab.domain.com' 12 | nginx['listen_https'] = false 13 | nginx['listen_port'] = 80 14 | gitlab_rails['backup_upload_remote_directory'] = 's3-backup-bucket' 15 | gitlab_rails['smtp_enable'] = true 16 | gitlab_rails['smtp_address'] = "smtp.domain.com" 17 | gitlab_rails['smtp_port'] = 587 18 | gitlab_rails['smtp_user_name'] = "gitlab@domain.com" 19 | gitlab_rails['smtp_password'] = "EMAILPASSWORD" 20 | gitlab_rails['smtp_domain'] = "domain.com" 21 | gitlab_rails['smtp_authentication'] = "plain" 22 | gitlab_rails['smtp_enable_starttls_auto'] = true 23 | gitlab_rails['gitlab_shell_ssh_port'] = 22 24 | volumes: 25 | - gitlab-config:/etc/gitlab 26 | - gitlab-logs:/var/log/gitlab 27 | - gitlab-data:/var/opt/gitlab 28 | networks: 29 | - traefik-proxy 30 | - default 31 | labels: 32 | - traefik.enable=true 33 | - traefik.http.routers.gitlab_insecure.entrypoints=web 34 | - traefik.http.routers.gitlab_insecure.rule=Host(`gitlab.domain.com`) 35 | - traefik.http.routers.gitlab_insecure.middlewares=redirect@file 36 | 37 | - traefik.http.routers.gitlab.entrypoints=web-secure 38 | - traefik.http.routers.gitlab.rule=Host(`gitlab.domain.com`) 39 | - traefik.http.routers.gitlab.tls.certresolver=letsencrypt 40 | - traefik.http.services.gitlab.loadbalancer.server.port=80 41 | - traefik.docker.network=traefik-proxy 42 | 43 | # Can't filter TCP traffic on SNI, see link below 44 | # https://community.containo.us/t/routing-ssh-traffic-with-traefik-v2/717/6 45 | - traefik.tcp.routers.gitlab-ssh.rule=HostSNI(`*`) 46 | - traefik.tcp.routers.gitlab-ssh.entrypoints=ssh 47 | - traefik.tcp.routers.gitlab-ssh.service=gitlab-ssh-svc 48 | - traefik.tcp.services.gitlab-ssh-svc.loadbalancer.server.port=22 49 | 50 | networks: 51 | traefik-proxy: 52 | external: true 53 | 54 | volumes: 55 | gitlab-config: 56 | gitlab-logs: 57 | gitlab-data: 58 | -------------------------------------------------------------------------------- /nexus/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | frontend: 5 | image: sonatype/nexus3 6 | container_name: nexus_frontend 7 | volumes: 8 | - nexus-data:/nexus-data 9 | networks: 10 | - traefik-proxy 11 | - default 12 | labels: 13 | - traefik.enable=true 14 | - traefik.http.routers.nexus_insecure.entrypoints=web 15 | - traefik.http.routers.nexus_insecure.rule=Host(`nexus.domain.com`) 16 | - traefik.http.routers.nexus_insecure.middlewares=redirect@file 17 | 18 | - traefik.http.routers.nexus.entrypoints=web-secure 19 | - traefik.http.routers.nexus.rule=Host(`nexus.domain.com`) 20 | - traefik.http.routers.nexus.tls.certresolver=letsencrypt 21 | - traefik.http.services.nexus.loadbalancer.server.port=8081 22 | - traefik.docker.network=traefik-proxy 23 | restart: unless-stopped 24 | 25 | networks: 26 | traefik-proxy: 27 | external: true 28 | 29 | volumes: 30 | nexus-data: 31 | 32 | -------------------------------------------------------------------------------- /portainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | frontend: 5 | image: portainer/portainer 6 | container_name: portainer_frontend 7 | volumes: 8 | - /var/run/docker.sock:/var/run/docker.sock 9 | - portainer_data:/data 10 | networks: 11 | - traefik-proxy 12 | - default 13 | labels: 14 | - traefik.enable=true 15 | - traefik.http.routers.portainer_insecure.entrypoints=web 16 | - traefik.http.routers.portainer_insecure.rule=Host(`portainer.domain.com`) 17 | - traefik.http.routers.portainer_insecure.middlewares=redirect@file 18 | 19 | - traefik.http.routers.portainer.entrypoints=web-secure 20 | - traefik.http.routers.portainer.rule=Host(`portainer.domain.com`) 21 | - traefik.http.routers.portainer.tls.certresolver=letsencrypt 22 | - traefik.http.services.portainer.loadbalancer.server.port=9000 23 | - traefik.docker.network=traefik-proxy 24 | restart: unless-stopped 25 | 26 | networks: 27 | traefik-proxy: 28 | external: true 29 | 30 | volumes: 31 | portainer_data: 32 | -------------------------------------------------------------------------------- /rocketchat/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | rocketchat: 5 | image: rocketchat/rocket.chat:latest 6 | command: > 7 | bash -c 8 | "for i in `seq 1 30`; do 9 | node main.js && 10 | s=$$? && break || s=$$?; 11 | echo \"Tried $$i times. Waiting 5 secs...\"; 12 | sleep 5; 13 | done; (exit $$s)" 14 | restart: unless-stopped 15 | volumes: 16 | - uploads:/app/uploads 17 | environment: 18 | - PORT=3000 19 | - ROOT_URL=http://localhost:3000 20 | - MONGO_URL=mongodb://mongo:27017/rocketchat 21 | - MONGO_OPLOG_URL=mongodb://mongo:27017/local 22 | - MAIL_URL=smtp://smtp.email 23 | depends_on: 24 | - mongo 25 | expose: 26 | - "3000" 27 | labels: 28 | - traefik.enable=true 29 | - traefik.http.routers.rocket_insecure.entrypoints=web 30 | - traefik.http.routers.rocket_insecure.rule=Host(`rocket.domain.com`) 31 | - traefik.http.routers.rocket_insecure.middlewares=redirect@file 32 | 33 | - traefik.http.routers.rocket.entrypoints=web-secure 34 | - traefik.http.routers.rocket.rule=Host(`rocket.domain.com`) 35 | - traefik.http.routers.rocket.tls.certresolver=letsencrypt 36 | - traefik.http.services.rocket.loadbalancer.server.port=3000 37 | - traefik.docker.network=traefik-proxy 38 | networks: 39 | - traefik-proxy 40 | - default 41 | 42 | mongo: 43 | image: mongo:4.0 44 | restart: unless-stopped 45 | volumes: 46 | - db:/data/db 47 | #- ./data/dump:/dump 48 | command: mongod --smallfiles --oplogSize 128 --replSet rs0 --storageEngine=mmapv1 49 | labels: 50 | - "traefik.enable=false" 51 | 52 | # this container's job is just run the command to initialize the replica set. 53 | # it will run the command and remove himself (it will not stay running) 54 | mongo-init-replica: 55 | image: mongo:3.4 56 | command: > 57 | bash -c 58 | "for i in `seq 1 30`; do 59 | mongo mongo/rocketchat --eval \" 60 | rs.initiate({ 61 | _id: 'rs0', 62 | members: [ { _id: 0, host: 'localhost:27017' } ]})\" && 63 | s=$$? && break || s=$$?; 64 | echo \"Tried $$i times. Waiting 5 secs...\"; 65 | sleep 5; 66 | done; (exit $$s)" 67 | depends_on: 68 | - mongo 69 | 70 | # hubot, the popular chatbot (add the bot user first and change the password before starting this image) 71 | hubot: 72 | image: rocketchat/hubot-rocketchat:latest 73 | restart: unless-stopped 74 | environment: 75 | - ROCKETCHAT_URL=rocketchat:3000 76 | - ROCKETCHAT_ROOM=GENERAL 77 | - ROCKETCHAT_USER=bot 78 | # you can add more scripts as you'd like here, they need to be installable by npm 79 | - EXTERNAL_SCRIPTS=hubot-help,hubot-seen,hubot-links,hubot-diagnostics 80 | depends_on: 81 | - rocketchat 82 | labels: 83 | - "traefik.enable=false" 84 | volumes: 85 | - scripts:/home/hubot/scripts 86 | # this is used to expose the hubot port for notifications on the host on port 3001, e.g. for hubot-jenkins-notifier 87 | ports: 88 | - 3001:8080 89 | 90 | networks: 91 | traefik-proxy: 92 | external: true 93 | 94 | volumes: 95 | db: 96 | scripts: 97 | uploads: 98 | -------------------------------------------------------------------------------- /traefik2/acme.json: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /traefik2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | 5 | traefik: 6 | image: traefik:latest 7 | container_name: traefik2 8 | command: 9 | - --log.level=DEBUG 10 | labels: 11 | - traefik.enable=true 12 | - traefik.http.routers.traefik.entrypoints=web 13 | - traefik.http.routers.traefik.rule=Host(`traefik.domain.com`) 14 | - traefik.http.routers.traefik.middlewares=redirect@file 15 | 16 | - traefik.http.routers.traefik_secure.entrypoints=web-secure 17 | - traefik.http.routers.traefik_secure.rule=Host(`traefik.domain.com`) 18 | - traefik.http.routers.traefik_secure.tls.certresolver=letsencrypt 19 | - traefik.http.routers.traefik_secure.service=api@internal 20 | - traefik.docker.network=traefik-proxy 21 | ports: 22 | - "2222:2222" # The SSH port 23 | - "80:80" # The HTTP port 24 | # - "8080:8080" # The Web UI (enabled by --api) 25 | - "443:443" # The HTTPS port 26 | volumes: 27 | - /var/run/docker.sock:/var/run/docker.sock 28 | - ./traefik.toml:/etc/traefik/traefik.toml 29 | # Configuration for FILE Provider 30 | - ./provider_file.toml:/etc/traefik/provider_file.toml 31 | - ./acme.json:/acme.json 32 | networks: 33 | - default 34 | - traefik-proxy 35 | networks: 36 | traefik-proxy: 37 | external: true 38 | -------------------------------------------------------------------------------- /traefik2/provider_file.toml: -------------------------------------------------------------------------------- 1 | # provider_file.toml 2 | [http.middlewares] 3 | [http.middlewares.redirect.redirectscheme] 4 | scheme = "https" 5 | -------------------------------------------------------------------------------- /traefik2/traefik.toml: -------------------------------------------------------------------------------- 1 | [Global] 2 | CheckNewVersion = true 3 | SendAnonymousUsage = true 4 | 5 | [entryPoints] 6 | [entryPoints.web] 7 | address = ":80" 8 | 9 | [entryPoints.web-secure] 10 | address = ":443" 11 | 12 | [entryPoints.ssh] 13 | address = ":2222" 14 | 15 | [providers] 16 | 17 | [providers.file] 18 | filename = "/etc/traefik/provider_file.toml" 19 | 20 | [providers.docker] 21 | # Connection to docker host system (docker.sock) 22 | endpoint = "unix:///var/run/docker.sock" 23 | watch = true 24 | # This will hide all docker containers that don't have explicitly 25 | # set label to "enable" 26 | exposedbydefault = false 27 | swarmMode = false 28 | 29 | [API] 30 | Dashboard = true 31 | debug = true 32 | 33 | [certificatesResolvers.letsencrypt.acme] 34 | email = "you@email.com" 35 | storage = "./acme.json" 36 | 37 | [certificatesResolvers.letsencrypt.acme.httpChallenge] 38 | entryPoint = "web" 39 | --------------------------------------------------------------------------------