Docker socket proxy and Caddy reverse proxy

I am fairly new to docker, but completely new to Caddy. I’m eager to learn and hope others can help improve my understanding.

I’ve been trying to look up best practices for docker containers. One such information is to not pass the docker socket directly to containers: - /var/run/docker.sock:/var/run/docker.sock:ro Instead, to use a socket proxy. After reviewing documentation and a few guides, I decided to mostly follow this guide.

My Goals:

  • Expose a service (e.g. nextcloud) to the web
  • Setup a reverse proxy with Caddy (docker) to add protection
  • Setup with a domain name I own for easier user access

The Question

When adding this socket proxy best practice to the mix, I’m not quite sure how that works alongside a reverse proxy with Caddy.

For example, if a service needs access to the docker.sock and to the Caddy reverse proxy,

Background

I use a docker-compose file to set up the socket proxy, and I decided it might be good to keep it as its own docker-compose, so that multiple unrelated containers can interact and use it.

Socket Proxy docker-compose

networks:
  socket_proxy:
    name: socket_proxy
    ipam:
      config:
        - subnet: 172.100.0.0/24 #change subnet as necessary
services:
  socket-proxy:
    container_name: socket-proxy
    image: tecnativa/docker-socket-proxy
    restart: unless-stopped
    networks:
      - socket_proxy
    ports:
      - "127.0.0.1:2375:2375" # Port 2375 should only ever get exposed to the internal network.
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - PUID=1010
      - PGID=105
      - LOG_LEVEL=info # debug,info,notice,warning,err,crit,alert,emerg
      ## Variables match the URL prefix (i.e. AUTH blocks access to /auth/* parts of the API, etc.).
      # 0 to revoke access.
      # 1 to grant access.
      ## Granted by Default
      - EVENTS=1
      - PING=1
      - VERSION=1

Services docker-compose content
In theory, I then need to define the default network to point to the docker socket in each service’s docker-compose.

networks:
  default:
    name: socket_proxy
    external: true

And in each service

 environment: 
 - DOCKER_HOST=tcp://socket-proxy:2375
 networks:
  - socket_proxy

Detailed Question

So if I am setting up a Caddy reverse proxy, that would mean another network, correct?
How do I set a service to use a Caddy network for public web access, but the socket proxy network if it needs to access the docker.sock?

TLDR; put apps on the Caddy network, put only Docker-aware automation containers on the socket proxy network and only put a container on both networks if it genuinely needs both.
_

You can attach a container to more than one Docker network so the reverse-proxy network and socket-proxy network can stay separate.

I would model it somewhat like this:

  • caddy: only for Caddy and the services Caddy needs to proxy to
  • socket_proxy: only for containers that genuinely need Docker API access

Most app containers, including something like Nextcloud should usually only need the Caddy network:

services:
  nextcloud:
    image: nextcloud
    networks:
      - caddy

networks:
  caddy:
    external: true

Then Caddy can proxy to it by service name:

cloud.example.com {
    reverse_proxy nextcloud:80
}

If a service genuinely needs both public reverse-proxy access and Docker API access, attach it to both networks:

services:
  example:
    image: example/image
    environment:
      - DOCKER_HOST=tcp://socket-proxy:2375
    networks:
      - caddy
      - socket_proxy

networks:
  caddy:
    external: true
  socket_proxy:
    external: true

Caddy should only need the Docker socket or socket proxy if you are using Docker-label-based dynamic configuration such as caddy-docker-proxy. If you are writing a normal Caddyfile manually, Caddy does not need Docker API access.

I would also avoid publishing the socket proxy port to the host unless you specifically need host access for some reason:

ports:
  - "127.0.0.1:2375:2375"

For container-to-container use that is usually unnecessary. Containers on the socket_proxy network can reach it as:

tcp://socket-proxy:2375

Thank you for such a detailed response. I will try all that you’ve noted and report back!

No worries at all. Hopefully that should be enough but if not, feel free to reply.

You should be able to mark my earlier reply as a solution as well to my knowledge.

So far so good.
One thing I did have to change: Originally, I had defined a non-root user in my socket proxy docker compose file. But the socket proxy requires root access, so I removed the PUID and PGID, defaulting it to the root account.