Running a Cells container behind a Traefik reverse proxy
Created on 2023/03/06,In this tutorial, we explain how to configure a Traefik v2 reverse proxy for your Cells Docker container and what settings are the most important to change.
Traefik v2 is a very efficient Go reverse proxy designed to perfectly integrate with Docker and Kubernetes.
For the purpose of this deployment, we use docker compose
: it offers a simple way to visualize the full stack deployment under a single file.
Quick start on localhost
version: "3"
services:
reverse:
image: traefik:2.10
ports: ["80:80"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
command:
- --providers.docker
- --api
- --entrypoints.web.address=:80
labels:
- traefik.http.routers.reverse.service=api@internal
- traefik.http.routers.reverse.rule=PathPrefix(`/api`)||PathPrefix(`/dashboard`)
- traefik.http.routers.reverse.entrypoints=web
cells:
image: pydio/cells:latest
expose: [8080]
environment:
- CELLS_BIND=0.0.0.0:8080
- CELLS_EXTERNAL=http://localhost
- CELLS_NO_TLS=1
labels:
- traefik.http.routers.cells.rule=Host(`localhost`)
- traefik.http.routers.cells.entrypoints=web
mysql:
image: mysql:8
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=cells
- MYSQL_DATABASE=cells
- MYSQL_USER=pydio
- MYSQL_PASSWORD=cells
cap_add:
- SYS_NICE
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --default_authentication_plugin=mysql_native_password
You can then access the Cells installer on http://localhost and the Traefik dashboard at http://localhost/dashboard/ (the trailing slash is important or you get a 404 - Page not found exception).
For live environments
This file sets up a production ready Cells ecosystem with opiniated configuration.
It exposes various services via HTTPS (provided by Let's Encrypt) under $PUBLIC_FQDN:
- The Pydio Cells server at the root
- The Traefik dashboard under
/dashboard/
- Promotheus metrics under
/prometheus
Both Traefik and Prometheus endpoints are protected by basic authentication with login/password: admin/admin
Do not forget to prepare / reset acme.json file when changing the Let's Encrypt configiguration,typically at first start or when switching from staging to prod CA server:
touch acme.json; chmod 600 acme.json
Note that we do not do the TLS termination of the requests for Cells at the Traefik level in order to enable gRPC communication between the End-User and the Cells application (necessary for the Sync client typically). If you do not need that, refer to the example above to perform TLS termination at the reverse proxy layer and thus have a simpler and easier to maintain configuration.
version: "3"
volumes:
cells_working_dir: {}
cells_data: {}
mysql_data: {}
prometheus_data: {}
services:
# Traefik as reverse proxy with dashboard enabled
reverse:
image: traefik:2.10
restart: unless-stopped
command:
# More logs when debugging
#- --log.level=DEBUG
# Tell traefik to watch docker events for hot reload
- --providers.docker
- --providers.docker.exposedbydefault=false
# Enable the dashboard on https
- --api
# Listen default HTTP ports
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Trust all certificates that are exposed by the downstream services, this is
# necessary to accept the default self-signed cert exposed by the Cells service.
- --serverstransport.insecureskipverify=true
# Automatic generation of certificate with Let's Encrypt
- --certificatesresolvers.leresolver.acme.email=${TLS_MAIL_ADDRESS}
- --certificatesresolvers.leresolver.acme.storage=/acme.json
- --certificatesresolvers.leresolver.acme.tlschallenge=true
# Insure to use staging CA server while testing to avoid being black listed => generated cert is un-trusted by browsers. Comment out once everything is correctly configured.
- --certificatesresolvers.leresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Persists certificate locally, otherwise we will recreate new ones at each restarts and quickly hit limits.
# Remember to flush the file if you want to switch from staging CA server to prod
- ./acme.json:/acme.json
labels:
# Redirect HTTP traffic to HTTPS
- traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)
- traefik.http.routers.redirs.entrypoints=web
- traefik.http.routers.redirs.middlewares=redirect-to-https
- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
# Expose the traefik dashboard on the reserved sub path, TLS is provided by the Let's Encrypt cert provider.
- traefik.enable=true
- traefik.http.routers.reverse.service=api@internal
- traefik.http.routers.reverse.rule=PathPrefix(`/api`)||PathPrefix(`/dashboard`)
- traefik.http.routers.reverse.entrypoints=websecure
- traefik.http.routers.reverse.tls.certresolver=leresolver
# Protect dashboard with simple auth => log with admin / admin for this example
- traefik.http.routers.reverse.middlewares=admin
# Password is generated with `htpasswd -nb admin admin` beware to escape all '$' replacing them by '$$'
- "traefik.http.middlewares.admin.basicauth.users=admin:$$apr1$$KnKvATsN$$L8K.P.maCu4zR/rVzD8h0/"
# DB backend
mysql:
image: mysql:8
restart: unless-stopped
volumes:
- mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=cells
- MYSQL_USER=${MYSQL_CELLS_USER_LOGIN}
- MYSQL_PASSWORD=${MYSQL_CELLS_USER_PASSWORD}
command: [mysqld, --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci]
# Pydio Cells app
cells:
image: ${CELLS_DOCKER_IMAGE}
restart: unless-stopped
# Not compulsory but it eases some of the maintenance operations.
hostname: cells
expose:
- 443
volumes:
- cells_working_dir:/var/cells
- cells_data:/data
- ./metrics:/var/cells/services/pydio.gateway.metrics
environment:
- CELLS_WORKING_DIR=/var/cells
- CELLS_DATA=/data
- CELLS_BIND=${PUBLIC_FQDN}:443
- CELLS_EXTERNAL=https://${PUBLIC_FQDN}
- CELLS_ENABLE_METRICS=true
- CELLS_METRICS_BASIC_AUTH=admin:admin
labels:
- traefik.enable=true
- traefik.http.services.cells.loadbalancer.server.scheme=https
- traefik.http.routers.cells.rule=Host(`${PUBLIC_FQDN}`)
- traefik.http.routers.cells.entrypoints=websecure
- traefik.http.routers.cells.tls=true
- traefik.http.routers.cells.tls.certresolver=leresolver
depends_on:
- mysql
# Prometheus to expose metrics
prometheus:
image: prom/prometheus
expose:
- 9090
volumes:
- prometheus_data:/prometheus
- ./prom.yml:/etc/prometheus/prometheus.yml
- ./metrics:/etc/prometheus/watch:ro
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=30d
- --web.external-url=https://${PUBLIC_FQDN}/prometheus
- --web.listen-address=:9090
labels:
# Expose the metrics on the reserved sub path, TLS is provided by the Let's Encrypt cert provider.
- traefik.enable=true
- traefik.http.routers.prometheus.rule=Host(`${PUBLIC_FQDN}`)&&PathPrefix(`/prometheus`)
- traefik.http.routers.prometheus.entrypoints=websecure
- traefik.http.services.prometheus.loadbalancer.server.port=9090
- traefik.http.routers.prometheus.tls.certresolver=leresolver
# Protect metrics entry point with simple auth
- traefik.http.routers.prometheus.middlewares=admin
In order to use the above file, you must add a .env
and a prom.yml
file in the same folder with following content:
CELLS_DOCKER_IMAGE=pydio/cells-enterprise:latest
PUBLIC_FQDN=share.example.com
TLS_MAIL_ADDRESS=tls@example.com
MYSQL_ROOT_PASSWORD=cells
MYSQL_CELLS_USER_LOGIN=pydio
MYSQL_CELLS_USER_PASSWORD=cells
# Simple prometheus configuration that watches itself and the entry points declared by cells:
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
metrics_path: '/prometheus/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['prometheus:9090']
- job_name: 'cells'
scheme: https
basic_auth:
username: "admin"
password: "admin"
tls_config:
insecure_skip_verify: true
http_sd_configs:
- url: "https://cells/metrics/sd"
basic_auth:
username: "admin"
password: "admin"
tls_config:
insecure_skip_verify: true
A few more hints about the docker-compose file
For reverse
:
ports
: our reverse proxy listens incoming requests on port 443volumes
:- in order to listen to docker events, traefik containers needs to access our machine docker socket
- we also setup a permanent volume so the SSL certificates are not regenerated every time we relaunch our container
command
:provider
: we link traefik routing system to dockerentrypoints
: we namewebsecure
our link to port 443certificateresolvers
:- we name
leresolver
the certificate issuing process, set it up totlschallenge
- meaning that Let's encrypt checks the domain is ours by connecting to our server on port 443 -, provide a valid email to complete the certificate issuing, and define the path in the container where the certificate is stored / found - in case the machine is not accessible from outside your network, you might use
dnschallenge
instead oftlschallenge
For cells
:
expose
: our cells instance is configured to listen on port 443, but this port is not exposed outside of docker.environment
:CELLS_BIND
: address where the application http server is bound to - our cell container accepts requests addressed to$PUBLIC_FQDN:443
, with a self signed certificate that is managed by CellsCELLS_EXTERNAL
: url the end user uses to connect to the application - in our case, by typinghttps://$PUBLIC_FQDN
in a web browser
labels
:- we name
cells
the configuration we are going to use, and specify it is to be used for requests searching to reach the host. - we specify that the requests that are served are the ones reaching the entrypoint
websecure
- that we have defined in traefik, listening on port 443 - we define that the SSL communication for this host is using the resolver
leresolver
we configured in traefik - Note: the
extra_hosts
option can be used if there is no DNS entry accessible by the Cells container matching the FQDN: and if you are using an old version of Cells (prior to 2.1). Cells must be able to access itself via theCELLS_BIND
address.extra_hosts
directive simply add a new entry to the/etc/hosts
file of the container.
- we name
See Also