Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions examples/traefik-new/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Docker Registry with Traefik, Redis, and UI on Docker Swarm

This guide walks you through deploying a private Docker Registry with authentication and a UI, using Traefik as a reverse proxy and Redis for caching, all running in Docker Swarm mode.

## Prerequisites

* Docker and Docker Compose installed
* Docker Swarm initialized on your system (use docker swarm init to initialize)
* Traefik setup and working in your Swarm environment
* A domain (e.g., my-registry.example.com, my-registry-ui.example.com, my-traefik.example.com) pointing to your Swarm cluster

## Deployment Sources

This deployment consists of four services:

1. Traefik: A reverse proxy to route requests to the registry and the UI.
2. Docker Registry: The registry for storing and managing Docker images.
3. Redis: A caching service for Docker Registry’s blob descriptors. (OPTIONAL)
4. Docker Registry UI: A web UI for viewing and managing images in your Docker registry.


> [!IMPORTANT]
> In all configuration files, be sure to review and update all the lines with `# !!!!CHANGEME!!!!` directives with your specific values.

### 1. Traefik Reverse Proxy

The relevant file is `docker-compose-traefik.yml`, which configures Traefik to handle routing and SSL termination for both the Docker registry and the UI.

* `traefik.http.routers.traefik-public-https.rule=Host(my-traefik.example.com)` used as placeholder for traefik dashboard URL
* `<</PATH/TO/YOUR/HTPASSWD>>` used as a placeholder for your `htpasswd` to be mounted as `/auth/.htpasswd`
* `htpasswd -c /path/to/your/htpasswd my-auth-username` then enter the password you want for basic auth
* Needs `sudo apt-get install apache2-utils` if ubuntu
* Needs `brew install httpd` if OSX
* `<</PATH/TO/YOUR/TRAEFIK/CONFIG/FOLDER/conf.d>>` is the placeholder for [traefik dynamic configuration files](https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-dynamic-configuration) directory, which is mounted on `/conf.d` on traefik container.
* This is optional, you can remove it. If you do so, also remove `--providers.file.directory=/conf.d/` from the `command` section.
* `--certificatesresolvers.le.acme.email=my-le-email.example.com` used as placeholder for let's encrypt email.

This configuration sets up Traefik to listen for requests on port 80 (HTTP) and 443 (HTTPS), with automatic SSL certificate generation using Let's Encrypt.

### 2. Docker Registry

The relevant file is `docker-compose-registry.yml`, to deploy it with Redis for blob descriptor caching, basic authentication with htpasswd, and with necessary `CORS` settings to enable the use of UI.

* `<</PATH/TO/YOUR/REGISTRY/STORAGE>>` used as placeholder to indicate directory for docker storage, which is mounter on `/var/lib/docker` in the registry container.
* `<</PATH/TO/YOUR/HTTPASSWD>>` used as a placeholder for your `htpasswd` to be mounted as `/auth/.htpasswd`
* You can use the same both for traefik (see above) and registry, create two separate `htpasswd` keys, up to your discretion.
* If you don't need redis, remove the following lines:
```diff
- REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis # OPTIONAL (see docker-compose-redis.yml) remove if not needed
- REGISTRY_REDIS_ADDR: redis:6379 # OPTIONAL remove if not needed
```
* ` - traefik.http.routers.registry.rule=Host(my-registry.example.com)` used as placeholder for your registry URL.
* `- traefik.http.routers.registry.middlewares=compress@file,large-upload@file,registry-ratelimit@file` some middlewares added to optimize registry. These are OPTIONAL, their role explained below:
* `compress@file` reduces the amount of data transferred between the server and clients (e.g., browsers or API consumers).
* `large-upload@file` to handle large file uploads, such as Docker image layers, to ensure smooth handling and avoid request timeouts or memory overloads.
* `registry-ratelimit@file` to implement rate-limiting on the registry, restricting the number of requests a client can make within a specific period.
* `traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://my-registry-ui.example.com` the UI URL which will be allowed for CORS.

### 3. Redis

The relevant file is `docker-compose-redis.yml`. Nothing much to change here other than resource allocation. This is OPTIONAL, if not included in the swarm deployment, make sure to remove it from `docker-compose-registry.yml` as well.

### 4. UI (finally)

The relevant configs and settings to deploy `joxit/docker-registry-ui:latest` are in `docker-compose-ui.yml`.

* `REGISTRY_URL=https://my-registry.example.com` under the `environment` section is used as placeholder for the URL of the docker registry.
* `traefik.http.routers.registry-ui.rule=Host(my-registry-ui.example.com)` under the `label` section used as the placeholder for the URL over which the UI will be served.

## Deploy the stack

It is up to you to decide whether you want to deploy all services to the same stack or to individual stacks. The choice depends on how you prefer to organize and manage your services within Docker Swarm. In this guide, I used `my-registry-swarm` as the stack name for deploying all services together, but you can customize this according to your needs.

Once you edited all the files, deploy your stack to Docker Swarm by running the following commands:

1. Initialize Docker Swarm (if not already done):

```
docker swarm init
```

2. Deploy traefik

```
docker stack deploy -c docker-swarm-traefik.yml my-registry-swarm
```

3. Deploy redis (OPTIONAL)

```
docker stack deploy -c docker-swarm-redis.yml my-registry-swarm
```

4. Deploy docker registry

```
docker stack deploy -c docker-swarm-registry.yml my-registry-swarm
```

5. Deploy UI

```
docker stack deploy -c docker-swarm-ui.yml my-registry-swarm
```

6. Check status

```
docker stack ps my-registry-swarm
```
```
docker stack services my-registry-swarm
```

## Access the services

* Traefik UI: https://my-traefik.example.com
* Registry: https://my-registry.example.com
* Registry UI: https://my-registry-ui.example.com


By following these steps, you now have a private Docker Registry, complete with authentication, caching via Redis, and a web UI, all routed through Traefik in Docker Swarm mode.

## Contributed by

[Agah Karakuzu](https://agah.dev) ([@agahkarakuzu](https://github.com/agahkarakuzu))
17 changes: 17 additions & 0 deletions examples/traefik-new/docker-compose-redis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
redis:
image: redis:alpine
restart: unless-stopped
networks:
- traefik-public
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 32M
command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru

networks:
traefik-public:
external: true
37 changes: 37 additions & 0 deletions examples/traefik-new/docker-compose-registry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: '3.3'
services:
registry:
image: registry:2
networks: [traefik-public]
volumes:
- <</PATH/TO/YOUR/REGISTRY/STORAGE>>:/var/lib/registry # !!!!CHANGEME!!!! (REQUIRED)
- <</PATH/TO/YOUR/HTTPASSWD>>:/auth/.htpasswd:ro # !!!!CHANGEME!!!! (REQUIRED)
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/.htpasswd
REGISTRY_STORAGE_DELETE_ENABLED: "no"
REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis # OPTIONAL (see docker-compose-redis.yml) remove if not needed
REGISTRY_REDIS_ADDR: redis:6379 # OPTIONAL remove if not needed
deploy:
labels:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
- traefik.http.routers.registry.rule=Host(`my-registry.example.com`) # !!!!CHANGEME!!!! (REQUIRED)
- traefik.http.routers.registry.entrypoints=https
- traefik.http.routers.registry.tls=true
- traefik.http.routers.registry.tls.certresolver=le
- traefik.http.services.registry.loadbalancer.server.port=5000
- traefik.http.services.registry.loadbalancer.passhostheader=true
- traefik.http.routers.registry.middlewares=compress@file,large-upload@file,registry-ratelimit@file # # !!!!CHANGEME!!!! (OPTIONAL)
# CORS FOR UI
- traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://my-registry-ui.example.com # !!!!CHANGEME!!!! (REQUIRED)
- traefik.http.middlewares.cors.headers.accesscontrolallowmethods=HEAD,GET,OPTIONS # ADD DELETE TO SUPPORT IMAGE DELETION
- traefik.http.middlewares.cors.headers.accesscontrolallowcredentials=true
- traefik.http.middlewares.cors.headers.accesscontrolallowheaders=Authorization,Accept,Cache-Control
- traefik.http.middlewares.cors.headers.accesscontrolexposeheaders=Docker-Content-Digest
- traefik.http.routers.registry.middlewares=cors
networks:
traefik-public:
external: true
105 changes: 105 additions & 0 deletions examples/traefik-new/docker-compose-traefik.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
version: '3.3'

services:

traefik:
image: traefik:v2.11.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v3 has been stable for over a year, please use a stable v3 version.

ports:
- target: 80
published: 80
- target: 443
published: 443
- target: 222
published: 222
deploy:
placement:
constraints:
# Make the traefik service run only on the node with this label
# as the node with it has the volume for the certificates
- node.labels.traefik-public.traefik-public-certificates == true
labels:
# Enable Traefik for this service, to make it available in the public network
- traefik.enable=true
# Use the traefik-public network (declared below)
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
# admin-auth middleware with HTTP Basic auth
- traefik.http.middlewares.admin-auth.basicauth.usersfile=/auth/.htpasswd
# https-redirect middleware to redirect HTTP to HTTPS
# It can be re-used by other stacks in other Docker Compose files
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
# traefik-http set up only to use the middleware to redirect to https
# Uses the environment variable DOMAIN
- traefik.http.routers.all-http.rule=HostRegexp(`{host:.+}`)
- traefik.http.routers.all-http.entrypoints=http
- traefik.http.routers.all-http.middlewares=https-redirect
# traefik-https the actual router using HTTPS
# Uses the environment variable DOMAIN
- traefik.http.routers.traefik-public-https.rule=Host(`my-traefik.example.com`) # !!!!CHANGEME!!!! (REQUIRED)
- traefik.http.routers.traefik-public-https.entrypoints=https
- traefik.http.routers.traefik-public-https.tls=true
# Use the special Traefik service api@internal with the web UI/Dashboard
- traefik.http.routers.traefik-public-https.service=api@internal
# Use the "le" (Let's Encrypt) resolver created below
- traefik.http.routers.traefik-public-https.tls.certresolver=le
# Enable HTTP Basic auth, using the middleware created above
- traefik.http.routers.traefik-public-https.middlewares=admin-auth
# Define the port inside of the Docker service to use
- traefik.http.services.traefik-public.loadbalancer.server.port=8080
volumes:
# Add Docker as a mounted volume, so that Traefik can read the labels of other services
- /var/run/docker.sock:/var/run/docker.sock:ro
- <</PATH/TO/YOUR/HTPASSWD>>:/auth/.htpasswd:ro # !!!!CHANGEME!!!! (REQUIRED)
# Mount the volume to store the certificates
- traefik-public-certificates_new:/certificates
- <</PATH/TO/YOUR/TRAEFIK/CONFIG/FOLDER/conf.d>>:/conf.d # # !!!!CHANGEME!!!! (OPTIONAL) use for dynamic conf. Remove if not needed.
command:
# - --log.level=DEBUG
# Enable Docker in Traefik, so that it reads labels from Docker services
- --providers.docker
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In traefik v3, the docker provider has been deprecated for swarm. Please update all references as appropriate.

Copy link
Author

@agahkarakuzu agahkarakuzu Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jnovack I'll make ensure a proper v2 --> v3 migration and will let you know when done.

# Add a constraint to only use services with the label "traefik.constraint-label=traefik-public"
- --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
# Do not expose all Docker services, only the ones explicitly exposed
- --providers.docker.exposedbydefault=false
# Enable Docker Swarm mode
- --providers.docker.swarmmode
# file provider
- --providers.file.directory=/conf.d/ # !!!!CHANGEME!!!! (OPTIONAL) see the last entry of volumes. Remove if not needed.
- --providers.file.watch=true
# Create an entrypoint "http" listening on port 80
- --entrypoints.http.address=:80
# Create an entrypoint "https" listening on port 443
- --entrypoints.https.address=:443
# Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL
- --certificatesresolvers.le.acme.email=my-le-email.example.com # !!!!CHANGEME!!!! (REQUIRED)
# Store the Let's Encrypt certificates in the mounted volume
- --certificatesresolvers.le.acme.storage=/certificates/acme.json
# Use the TLS Challenge for Let's Encrypt
- --certificatesresolvers.le.acme.tlschallenge=true
# Enable the access log, with HTTP requests
- --accesslog
# Enable the Traefik log, for configurations and errors
- --log
- --log.level=INFO
# Enable the Dashboard and API
- --api
networks:
# Use the public network created to be shared between Traefik and
# any other service that needs to be publicly available with HTTPS
- traefik-public
- traefik-external

volumes:
# Create a volume to store the certificates, there is a constraint to make sure
# Traefik is always deployed to the same Docker node with the same volume containing
# the HTTPS certificates
traefik-public-certificates_new:

networks:
# Use the previously created public network "traefik-public", shared with other
# services that need to be publicly available via this Traefik
traefik-public:
external: true
traefik-external:
external: true
29 changes: 29 additions & 0 deletions examples/traefik-new/docker-compose-ui.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: '3.3'

services:
# Docker Registry UI
registry_ui:
image: joxit/docker-registry-ui:latest
environment:
- REGISTRY_URL=https://my-registry.example.com # !!!!CHANGEME!!!! (REQUIRED)
- SINGLE_REGISTRY=true
- DELETE_IMAGES=false # Disable delete functionality, you may enable
- NGINX_PROXY_PASS_URL=http://registry:5000 # The URL that the UI will proxy to the Docker registry
networks:
- traefik-public
deploy:
replicas: 1 # You can adjust the replica count as needed for scaling
labels:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
- traefik.http.routers.registry-ui.rule=Host(`my-registry-ui.example.com`) # # !!!!CHANGEME!!!! (REQUIRED) make sure that docker-compose-regitry.yml has the same
- traefik.http.routers.registry-ui.entrypoints=https
- traefik.http.routers.registry-ui.tls=true
- traefik.http.routers.registry-ui.tls.certresolver=le
- traefik.http.services.registry-ui.loadbalancer.server.port=80
- traefik.http.services.registry-ui.loadbalancer.passhostheader=true

networks:
traefik-public:
external: true