Skip to content

Instantly share code, notes, and snippets.

@u1i
Forked from PieterScheffers/start_docker_registry.bash
Last active March 29, 2025 03:59
Show Gist options
  • Save u1i/1ec704b5c099b407875128fe0b864735 to your computer and use it in GitHub Desktop.
Save u1i/1ec704b5c099b407875128fe0b864735 to your computer and use it in GitHub Desktop.
Start docker registry with letsencrypt certificates and Basic Auth
#!/usr/bin/env bash
# install docker
# https://docs.docker.com/engine/installation/linux/ubuntulinux/
# install docker-compose
# https://docs.docker.com/compose/install/
# install letsencrypt
# https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04
# Generate SSL certificate for domain
/opt/letsencrypt/letsencrypt-auto certonly --keep-until-expiring --standalone -d domain.example.com --email [email protected]
# Setup letsencrypt certificates renewing
line="30 2 * * 1 /opt/letsencrypt/letsencrypt-auto renew >> /var/log/letsencrypt-renew.log"
(crontab -u root -l; echo "$line" ) | crontab -u root -
# Rename SSL certificates
# https://community.letsencrypt.org/t/how-to-get-crt-and-key-files-from-i-just-have-pem-files/7348
cd /etc/letsencrypt/live/domain.example.com/
cp privkey.pem domain.key
cat cert.pem chain.pem > domain.crt
chmod 777 domain.crt
chmod 777 domain.key
# Generate Password for Basic Auth
mkdir auth
docker run \
--entrypoint htpasswd \
registry:2 -Bbn testuser testpassword > auth/htpasswd
# https://docs.docker.com/registry/deploying/
docker run -d -p 443:5000 --restart=always --name registry \
-v /etc/letsencrypt/live/domain.example.com:/certs \
-v /opt/docker-registry:/var/lib/registry \
-v `pwd`/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Axway Docker Registry" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# List images
# https://domain.example.com/v2/_catalog
@flickerfly
Copy link

Oh, good point.

As there is a volume in use, destroy and rebuild is probably the simple fix, but yeah doing the cert name shuffle in cron would be better.

@mahmudulrm
Copy link

Thanks

@5andr0
Copy link

5andr0 commented Nov 11, 2024

cat cert.pem chain.pem > domain.crt
chmod 777 domain.crt
chmod 777 domain.key

This isn't necessary. Exactly this is stored in fullchain.pem
Just mount the fullchain.pem as /certs/domain.crt:ro

However renewing certs here makes no sense, because registry would need a full restart. There's no reload or renew check implemented yet: distribution/distribution#3712

Because of that, there's no way around a loadbalancer which auto renews the certs. I opted for traefik.
Here's my compose.yml:

services:

  traefik:
    image: "traefik:latest"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.websecure.address=:443"
      - "--entryPoints.websecure.transport.respondingTimeouts.readTimeout=1000"
      - "--entryPoints.websecure.transport.respondingTimeouts.writeTimeout=1000"
      #- "--entryPoints.websecure.transport.respondingTimeouts.idleTimeout=1000"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "[email protected]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./acme.json:/letsencrypt/acme.json"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  registry:
    user: "1000:1000"
    image: registry:2
    container_name: "registry"
    volumes:
      - ./registry:/var/lib/registry
    environment:
      - REGISTRY_HTTP_SECRET=randomsecret
      - REGISTRY_STORAGE_DELETE_ENABLED=true
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.registry-auth.basicauth.users=user:$$apr1$$/qfL7raM$$xg76wXJjTcFUPqIulDq8t1"
      - "traefik.http.routers.registry.middlewares=registry-auth"
      - "traefik.http.routers.registry.rule=Host(`registry.example.com`)"
      - "traefik.http.routers.registry.entrypoints=websecure"
      - "traefik.http.routers.registry.tls.certresolver=myresolver"
      - "traefik.http.services.registry.loadbalancer.server.port=5000"
      #- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=900000000" default is 0, so no limit set
    depends_on: ['traefik']

  ui:
    image: joxit/docker-registry-ui:latest
    container_name: "ui"
    environment:
        - DELETE_IMAGES=true
        - REGISTRY_TITLE=Example Docker Registry
        - NGINX_PROXY_PASS_URL=http://registry:5000
        - SINGLE_REGISTRY=true
    depends_on: ['registry']
    labels:
      traefik.enable: true
      traefik.http.middlewares.ui-auth.basicauth.users: user:$$apr1$$/qfL7raM$$xg76wXJjTcFUPqIulDq8t1
      traefik.http.routers.ui.middlewares: ui-stripprefixregex, ui-auth, ui-redirect
      traefik.http.routers.ui.entrypoints: websecure
      traefik.http.routers.ui.tls.certresolver: myresolver
      traefik.http.services.ui.loadbalancer.server.port: 80
      traefik.http.middlewares.ui-stripprefixregex.stripprefixregex.regex: /ui/
      traefik.http.middlewares.ui-redirect.redirectregex.regex: ^https://registry.example.com/ui$$
      traefik.http.middlewares.ui-redirect.redirectregex.replacement: https://registry.example.com/ui/
      traefik.http.middlewares.ui-redirect.redirectregex.permanent: false #we don't want the browser to store the redirect
      traefik.http.routers.ui.rule: Host(`registry.example.com`) && PathPrefix(`/ui`)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment