In this guide we’ll use Docker compose to setup FileBrowser Quantum and OnlyOffice behind Traefik Reverse Proxy for a secure production-ready deployment. You can use this guide as reference. If you want to copy-paste you also can, but remember to change all the fake info here with your own.
That said, this guide will cover:
✅ Automatic HTTPS and certificate renewal with Let’s Encrypt certificates, this is managed automatically by Traefik (every 60 days aprox).
✅ Secure JWT authentication between services (FileBrowser and OnlyOffice).
✅ Docker compose with Traefik labels configuration for the services.
✅ A Production-ready setup with proper security headers.
Basic understanding of Traefik, Docker, and Docker compose in general.
Email address for Let’s encrypt.
warning
You must have a valid domain name and configure your DDNS provider pointing to your domain for Let’s Encrypt to work. If your use Local IPs directly in Traefik, will not work.
However, if you don’t have you own domain, you can bind your local IP or hostname to your DDNS provider and use one of the free domains that they offer but I’m not sure if will work when exposing services publicly.
A quick overview of how will look your directory structure following the steps in the guide:
info
Note: The .env files on each directory of the services are environment files used to store sensitive information, they should go on the root folder of each service that you want to deploy, in this case, we are deploying three services Traefik, Filebrowser Quantum and OnlyOffice, so each directory should have its own .env file.
And make sure to replace all the info of the .env files with your own.
ip-range: Dynamic IP allocation for containers without static assignment - from 128 to 255.
label: Reserved IPs for containers that need them - from 2 to 127.
Which this command do is create a virtual docker network with the subnet 192.168.2.0/24 and reserve half of the IP addresses available (from 192.168.2.2 to 192.168.2.127). The reserved IPs are for containers that you don’t want to be changing its IP each time that you restart them, and how they are reserved, docker will not use them for its auto-allocation.
For assing static IPs to a container, you will need to specify the IP on the docker compose file of your service, below of the name of your docker network. For example:
services:filebrowser:image:gtstef/filebrowser:latestnetworks:proxy_network:# Name of the docker networkipv4_address:192.168.2.5# IP adress that you want to use with the service, the IP will be static, don't will change.
If you don’t want to reserve IPs, you can use this simplified version:
Docker will manage all auto-allocate all the IPs dynamically, and will assing a available network subnet automatically. You will not have static or reserved IPs, but is fine, on this guide we will use the containers with dynamic IPs, but if you want to use a static IP, you can add the ipv4 option on the docker compose files.
DOMAIN_NAME=your-domain.com # Your root domain here.DYNU_API_KEY=your-dynu-api-key-here # DDNS API Key (we are using dynu - check the traefik documentation about other providers)# Generate with: docker run --rm httpd:alpine htpasswd -nb your-username your-password | sed -e 's/\$/\$\$/g'TRAEFIK_DASHBOARD_CREDENTIALS=your-username:$$apr1$$5CK4UFEP$$gE8rmbSMI0Lfb9vPE9I2j/
Generate dashboard credentials:
Replace your-username and your-password with your own user and password, then paste the output in the .env file we created.
The acme.json file must have 600 permissions, this is for security. With that permissions only the Traefik process (and you user) is able to access (read an write) the certificates stored on that file.
Also BE SURE that you have a copy of that file, because there is where the certificates will be stored.
global:checkNewVersion:true# Check if new version of traefik is availablesendAnonymousUsage:falseapi:dashboard:trueinsecure:false# Log level. You can use DEBUG initially for see if the certificates are being pulled correctlylog:level:INFO# DEBUG, INFO, WARN, ERROR, FATAL, PANICentryPoints:web:address::80http:redirections:entryPoint:to:websecurescheme:httpspermanent:truewebsecure:address::443http:tls:certResolver:letsencrypttransport:respondingTimeouts:readTimeout:"300s"writeTimeout:"300s"idleTimeout:"360s"http2:maxConcurrentStreams:500asDefault:trueserversTransport:insecureSkipVerify:falseforwardingTimeouts:dialTimeout:"60s"# Time to establish TCP connectionresponseHeaderTimeout:"60s"# Time for backend to send headersidleConnTimeout:"90s"http:timeouts:readTimeout:"300s"writeTimeout:"300s"idleTimeout:"360s"tls:options:modern:minVersion:'VersionTLS13'default:minVersion:'VersionTLS12'cipherSuites:- 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'- 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'- 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'- 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'- 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305'- 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305'providers:docker:endpoint:"unix:///var/run/docker.sock"network:"proxy_network"exposedByDefault:falsefile:directory:"/etc/traefik/config"# Directory mounted in the compose file, here we will define our security headers for the services - Also called File Providerwatch:truecertificatesResolvers:letsencrypt:acme:email:your-email@provider.com # Replace with your emailstorage:/var/traefik/certs/acme.json# Use staging first for testing first:# caServer: https://acme-staging-v02.api.letsencrypt.org/directory# Then switch to production:caServer:https://acme-v02.api.letsencrypt.org/directory# certificates for productiondnsChallenge:provider:dynuresolvers:- "1.1.1.1:53"- "1.0.0.1:53"# Uncomment if having issues when pulling the certificates.# disablePropagationCheck: true# delayBeforeCheck: "60s"
warning
Important Configuration Updates:
Replace your-email@provider.com with your actual email.
Change dynu with your DDNS provider and update the traefik compose and .env if you do.
Start with staging caServer for testing, then switch to production. Let’s encrypt has a rate-limit, so if something goes wrong you are testing first.
The staging caServer will not give you valid TLS certificates (you will have a warning saying that the site is not secure), but you can see that they are coming from Let’s encrypt on your browser, so that is how you test that is working.
http:# Each middleware defined need to be referenced in each service docker-compose via labels to use them.middlewares:# Global security headerssecurity-headers:headers:frameDeny:truesslRedirect:truebrowserXssFilter:truecontentTypeNosniff:trueforceSTSHeader:truestsIncludeSubdomains:truestsPreload:truestsSeconds:15552000customFrameOptionsValue:"SAMEORIGIN"referrerPolicy:"strict-origin-when-cross-origin"customRequestHeaders:X-Forwarded-Proto:"https"X-Real-IP:"true"X-Forwarded-For:"true"# Same as security headers, but letting the site being embedded (allowing i-frames) which onlyoffice needs.no-frame-block:headers:sslRedirect:truebrowserXssFilter:truecontentTypeNosniff:trueforceSTSHeader:truestsIncludeSubdomains:truestsPreload:truestsSeconds:15552000referrerPolicy:"strict-origin-when-cross-origin"customRequestHeaders:X-Forwarded-Proto:"https"X-Real-IP:"true"X-Forwarded-For:"true"# This middleware is to disable buffering, only use it if you have issues with SSE or uploading large files to filebrowserlimit:buffering:maxRequestBodyBytes:0maxResponseBodyBytes:0memRequestBodyBytes:0memResponseBodyBytes:0# This middleware is more for internal services, like APIs or databases.security-minimal:headers:sslRedirect:truecontentTypeNosniff:truestsPreload:truecustomRequestHeaders:X-Forwarded-Proto:"https"
For test if traefik is working you can visit the dashboard of traefik at: https://traefik.your-domain.com (the domain that you used on the .env file), and log in with the credentials that you generated before.
If the page is working you did all correctly! Let’s go with Filebrowser and OnlyOffice now.
report
Once you tested that all is working on the staging server, before you change to production BE SURE that you deleted the acme.json file first before pulling the new production certificates. You can just delete the content inside, or recreate the file again:
services:onlyoffice:container_name:onlyofficeimage:onlyoffice/documentserverenvironment:TZ:$TZJWT_SECRET:$ONLYOFFICE_SECRETONLYOFFICE_HTTPS_HSTS_ENABLED:false#ports:# - '8081:80' # Commented out because traefik will handle it.labels:- "traefik.enable=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.entrypoints=websecure"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls.certresolver=letsencrypt"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls.options=default"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.rule=Host(${DOMAIN_NAME})"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.service=${TRAEFIK_SERVICE_NAME}"- "traefik.http.services.${TRAEFIK_SERVICE_NAME}.loadbalancer.server.port=${TRAEFIK_SERVICE_PORT}"- "traefik.http.services.${TRAEFIK_SERVICE_NAME}.loadbalancer.passhostheader=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.middlewares=no-frame-block@file"# no-frame-block header that we defined in the middlewares.yaml config file.# Headers for onlyoffice, see https://github.com/ONLYOFFICE/onlyoffice-nextcloud/issues/151 for details- "traefik.http.middlewares.${TRAEFIK_SERVICE_NAME}-headers.headers.accesscontrolalloworiginlist=*"networks:proxy_network:restart:unless-stoppednetworks:proxy_network:external:true
info
Critical for OnlyOffice:
accesscontrolalloworiginlist=*: Middleware required for CORS - without this onlyoffice don’t will work with filebrowser and you will run into issues.
ONLYOFFICE_HTTPS_HSTS_ENABLED: false: Since traefik will handle HTTPS, not OnlyOffice.
We should use the no-frame-block middleware instead of security-headers to allow onlyoffice to be embedded into FileBrowser.
TRAEFIK_SERVICE_NAME=onlyoffice
TRAEFIK_SERVICE_PORT=80DOMAIN_NAME=`office.yourdomain.com`# Don't forget the backticks, they are critical for traefikONLYOFFICE_SECRET=KDSpcNwKAos2moijgdfi9jf9wr3wek=# Your output of the openssl commandTZ=America/New_York # Your timezone
Similar to getting started docker guide but customized to work with traefik. Remember to change the volumes block with your actual data or folders (Sources), the ones here an example.
services:filebrowser:container_name:filebrowserimage:gtstef/filebrowser:latestenv_file:.envenvironment:FILEBROWSER_CONFIG:"data/config.yaml"FILEBROWSER_DATABASE:"data/database.db"FILEBROWSER_ADMIN_PASSWORD:${FILEBROWSER_ADMIN_PASSWORD}TZ:${TZ}volumes:- './data:/home/filebrowser/data'- '/files:/files'- '/other/path/media:/media'user:filebrowserlabels:- "traefik.enable=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.entrypoints=websecure"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls.certresolver=letsencrypt"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.tls.options=default"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.rule=Host(${DOMAIN_NAME})"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.service=${TRAEFIK_SERVICE_NAME}"- "traefik.http.services.${TRAEFIK_SERVICE_NAME}.loadbalancer.server.port=${TRAEFIK_SERVICE_PORT}"- "traefik.http.services.${TRAEFIK_SERVICE_NAME}.loadbalancer.passhostheader=true"- "traefik.http.routers.${TRAEFIK_SERVICE_NAME}.middlewares=security-headers@file"# Security headers, you can also add the 'limit' middleware if you have issues with large file uploads. 'security-headers@file,limit@file'networks:proxy_network:restart:unless-stoppednetworks:proxy_network:external:true
server:port:80baseURL:"/"sources:# Sources for filebrowser, the path should match your volume mount (of the right)- path:"/files"name:"Files"config:defaultEnabled:truecreateUserDir:true- path:"/media"name:"Media"config:defaultEnabled:falsecreateUserDir:false# Critical for OnlyOffice integrationexternalUrl:"https://files.yourdomain.com"internalUrl:"http://filebrowser:80"auth:tokenExpirationHours:6adminUsername:admin # Your admin username, change this.methods:password:enabled:trueminLength:5# Minimum lenght of the passwordsignup:falseuserDefaults:darkMode:truelocale:"en"disableOnlyOfficeExt:".txt .html .md"integrations:office:url:"https://office.yourdomain.com"# The URL to the OnlyOffice Server that you defined in the onlyoffice .envinternalUrl:"http://onlyoffice:80"# Optional internal URL, uses the container name to communicate.secret:"KDSpcNwKAos2moijgdfi9jf9wr3wek="# Should be the SAME as OnlyOffice secret (also in onlyoffice .env)viewOnly:false
warning
Critical Configuration:
server.externalUrl: Must match your FileBrowser domain configured in .env file.
server.internalUrl: Uses Docker service name onlyoffice and its internal port (80).
integrations.office.url: Must match your OnlyOffice domain configured on the .env file of OnlyOffice.
integrations.office.secret: Must match OnlyOffice JWT_SECRET exactly (also in the onlyoffice .env)
If you have issues with onlyoffice, you should enable the office debug mode in filebrowser. To do that go to settings -> profile settings -> File Viewer Options in UI and enable the debug mode.
After that open any document and check the debug window, also check the logs of both containers as well. See troubleshooting for more detailed solutions.
docker compose pull # Pull the latest image available of the service.docker compose up -d --force-recreate # Re-create the container and restart it automatically.
Also try uncommenting the last lines on the traefik.yaml config file.
And check the acme.json file. You should see the certificates there, for that, first navigate to the directory where your services are, then do this command:
If you have other issues related with onlyoffice and filebrowser, you should check the Office Troubleshooting guide, which covers some of the most commons issues with solutions.