Use Traefik 2 with Nginx, Apache, or CaddyServer to Serve Static Files

Setup a simple HTTP static file server behind Traefik. My specific use case is to serve static files (eg - PDF, excel, etc) from my blog that runs Ghost. However, the software itself does not support that. There are a number of HTTP servers, but we will choose three of the most popular ones.

These work well because they provide a nice drop-in to solve static file serving because it is small, fast, and will work well in a default configuration.

Now we will explore two ways to serve up files.

  1. Hosting on a subdomain (eg - files.example.com)
  2. Hosting on the blog's domain but in a subfolder (eg - example.com/static)

Use docker-compose and docker labels to configure Traefik's routing and middlewares.

In both scenarios:

  • Use the images from Docker Hub.
  • Bind-mount a local folder to host the files for the web server. See volumes: in the docker-compose.

On its own domain

You must have the DNS entry, files.example.com in this case, to point at the server. An example file download URL would be:

  • https://files.example.com/sales-pitch.pdf

All-in-one config (docker-compose)

This is a docker-compose.yml with configurations for all three servers. Simply comment out the ones you don't want and un-comment the ones you do. Bobs- your uncle.

    static:

        # nginx config
        image: nginx
        volumes:
            - ./files:/usr/share/nginx/html:ro

# uncomment to use Aapche HTTPD Server
#        image: httpd:2.4-alpine
#        volumes:
#            - ./files:/usr/local/apache2/htdocs:ro

# Uncomment to use Caddeserver
#        image: caddy/caddy:scratch
#        command: file-server --root /files --listen 0.0.0.0:80
#        volumes:
#            - ./files:/files:ro

        container_name: static-files
        restart: unless-stopped
        networks:
            - traefik
        labels:
            # Match on the hostname and the path
            - traefik.http.routers.nginx.rule=Host(`files.example.com`)
            - traefik.http.routers.nginx.tls=true
            - traefik.http.routers.nginx.tls.certresolver=le
            - traefik.http.services.nginx.loadbalancer.server.port=80

            # tell Traefik which middlewares we want to use on this container
            - traefik.http.routers.nginx.middlewares=gzip

On a specific URI path (/static) in an existing domain

It is only a matter of three lines that will enable this to live under /static Path. An example file download URL would be:

  • https://blog.example.com/static/sales-pitch.pdf

Just the parts you need

# Match on the hostname and the path
- traefik.http.routers.nginx.rule=(Host(`blog.example.com`) && Path(`/static`))

# Define a new middleware to strip the URL prefix before sending it to nginx
- traefik.http.middlewares.nginx-stripprefix.stripprefix.prefixes=/static

# tell Traefik which middlewares we want to use on this container
- traefik.http.routers.nginx.middlewares=gzip@docker,nginx-stripprefix@docker

Full docker-compose.yml

    static:

        # nginx config
        image: nginx
        volumes:
            - ./files:/usr/share/nginx/html:ro

# uncomment to use Aapche HTTPD Server
#        image: httpd:2.4-alpine
#        volumes:
#            - ./files:/usr/local/apache2/htdocs:ro

# Uncomment to use Caddeserver
#        image: caddy/caddy:scratch
#        command: file-server --root /files --listen 0.0.0.0:80
#        volumes:
#            - ./files:/files:ro

        container_name: static-files
        restart: unless-stopped
        networks:
            - traefik
        labels:
            # Match on the hostname and the path
            - traefik.http.routers.static-files.rule=(Host(`blog.example.com`) && Path(`/static`))
            - traefik.http.routers.static-files.tls=true
            - traefik.http.routers.static-files.tls.certresolver=le
            - traefik.http.services.static-files.loadbalancer.server.port=80

            # Define a new middleware to strip the URL prefix before sending it to static-files
            - traefik.http.middlewares.static-files-stripprefix.stripprefix.prefixes=/static

            # tell Traefik which middlewares we want to use on this container
            - traefik.http.routers.static-files.middlewares=gzip@docker,static-files-stripprefix@docker

Things to note

  • The CaddyServer config needed a command: attribute added to tell it to activate its fileserver feature.
  • The mountpoints for volumes: is different per server
  • Only official images are used. Your milage may vary if you stray from these...

Conclusion

Using the above snippets you should be able to add Nginx, Apache HTTPD, or CaddyServer to your docker / Traefik deployments and have simple, no fuss static file serving.

I should also mention that is snipped is part of my production stack template that use for most Simple CTO projects. More on that later