Roxy can automatically discover Docker Compose services and
register them as .roxy domains. When enabled, starting a
compose stack instantly makes services available at
https://<service>.<project>.roxy.
Add the [docker] section to your Roxy user configuration
(~/Library/Application Support/Roxy/config.toml on macOS or
$HOME/.config/roxy/config.toml on Linux):
[docker]
enabled = trueThen restart the daemon:
roxy restartRoxy connects to the Docker socket and watches for container lifecycle events. When a container starts or stops, Roxy automatically updates its routing table.
When a container starts, Roxy evaluates it using these rules (in order):
roxy.enable=falselabel -- skip (explicit opt-out)roxy.enable=truelabel -- register (explicit opt-in)- Compose labels + exposed port -- register automatically
- Otherwise -- skip
For compose services, the domain is derived from the project
and service names: <service>.<project>.roxy. For example,
a service named web in project myapp becomes
web.myapp.roxy.
- The container must have at least one published port
(
ports:mapping indocker-compose.yml) - Roxy proxies to the host port, not the container port
- If a container exposes multiple ports, set
roxy.portto pick one (see Labels Reference)
Given this docker-compose.yml:
services:
web:
build: .
ports:
- "3000:3000"# Enable Docker integration in Roxy config
# (add [docker] enabled = true, then restart)
# Start your compose stack
docker compose up -d
# Roxy auto-discovers and registers web.myproject.roxy
# (project name comes from directory name by default)
open https://web.myproject.roxyNo roxy register needed -- it happens automatically.
Control Roxy behavior with container labels, either in
docker-compose.yml or via docker run --label.
| Label | Values | Description |
|---|---|---|
roxy.enable |
true / false |
Force opt-in or opt-out |
roxy.domain |
e.g. app.roxy |
Override the auto-generated domain |
roxy.port |
e.g. 8080 |
Pick which container port to proxy |
roxy.wildcard |
true |
Register as wildcard (*.domain) |
Custom domain:
services:
web:
build: .
ports:
- "3000:3000"
labels:
roxy.domain: "myapp.roxy"Explicit opt-in (non-compose container):
services:
standalone:
image: nginx
ports:
- "8080:80"
labels:
roxy.enable: "true"
roxy.domain: "nginx.roxy"Multiple ports -- pick one:
services:
api:
build: .
ports:
- "3000:3000"
- "9090:9090"
labels:
roxy.port: "3000"Wildcard subdomains:
services:
web:
build: .
ports:
- "3000:3000"
labels:
roxy.wildcard: "true"This registers *.web.myproject.roxy, so
anything.web.myproject.roxy routes to the container.
Opt out a service:
services:
db:
image: postgres
ports:
- "5432:5432"
labels:
roxy.enable: "false"Roxy determines the domain in this order:
roxy.domainlabel -- used as-is (must end with.roxy)- Compose labels --
<service>.<project>.roxy
The compose project name defaults to the directory name. You
can set it explicitly with COMPOSE_PROJECT_NAME or the
name: key in docker-compose.yml:
name: myapp
services:
web:
build: .
ports:
- "3000:3000"
# Domain: web.myapp.roxyWhen one container needs to reach another container's
.roxy domain (or any .roxy domain served by the host),
Docker's default DNS won't resolve .roxy names. Use
extra_hosts to point the domain at the host:
services:
web:
build: .
ports:
- "3000:3000"
worker:
build: .
extra_hosts:
- "web.myproject.roxy:host-gateway"host-gateway resolves to the host machine's IP (typically
host.docker.internal on Docker Desktop). The worker
container can now reach http://web.myproject.roxy through
Roxy on the host.
Add one entry per .roxy domain the container needs to
access.
Note: Wildcard
extra_hosts(e.g.,*.roxy) are not supported --/etc/hostsdoes not allow wildcards. You must list each domain explicitly.
On Linux, Docker containers share the host network
namespace (or use a bridge with direct host access). DNS
resolution and HTTP connectivity work without extra_hosts
in most setups.
On macOS, Docker Desktop runs containers inside a Linux
VM. Containers cannot reach the host via its LAN IP, but can
reach it via host-gateway / host.docker.internal. The
extra_hosts approach above is required for container-to-Roxy
communication.
Check which Docker containers Roxy has discovered:
roxy listDocker-discovered domains show their source as "external" in
the output. They appear alongside manually registered domains
but cannot be edited with roxy register or roxy route --
they are managed entirely by the Docker watcher.
View discovery logs:
# After setting daemon.log_level = "debug" in the Roxy config
roxy restart
# Or check the log file
roxy logs -fExample log output:
INFO Docker integration enabled
INFO Docker domain added domain=web.myapp.roxy target=127.0.0.1:3000
INFO Docker reconciliation complete added=1 removed=0 total=1
Check that:
- Docker integration is enabled (
[docker] enabled = true) - The container has published ports (
ports:in compose) - The container is not opted out (
roxy.enable=false) - The daemon is running (
roxy status)
Set daemon.log_level = "debug" in the Roxy config, restart,
and follow the log to see why a container was skipped:
roxy restart
roxy logs -fLook for Docker container skipped messages with a reason.
Roxy needs a host port mapping to proxy traffic. Make sure
your service has a ports: entry:
services:
web:
build: .
# This is required:
ports:
- "3000:3000"
# EXPOSE alone is not enoughIf a container exposes more than one port and no roxy.port
label is set, Roxy skips it (ambiguous). Add the label:
labels:
roxy.port: "3000"Roxy connects to the Docker socket
(/var/run/docker.sock by default) as your developer account.
If you see connection errors, verify the socket exists and that
your account can access it. On Linux this commonly means adding
the account to the docker group (then logging in again) or
using a rootless Docker socket.