A lightweight service registry bridge between Docker and Consul.
ServiceRegistrator automatically registers and deregisters services in Consul as Docker containers start and stop. It monitors the Docker event stream in real-time and keeps Consul's service catalog in sync with the running containers on a host.
This project is a Python re-implementation of
Gliderlabs Registrator, which is
no longer actively maintained. It is partly compatible with Registrator's
SERVICE_* environment variable syntax, making migration straightforward.
Key differences from Gliderlabs Registrator:
- Explicit registration only: containers must define a
SERVICE_NAME(orSERVICE_<port>_NAME) to be registered. Unnamed services are skipped, giving you full control over what appears in Consul. - Written in Python: easier to extend, debug, and contribute to.
- Service aliases: register a service under an additional name with an alias health check, useful for renaming services without breaking existing Consul templates.
- Periodic resync: optionally re-synchronize all services on a timer to recover from transient Consul issues.
- Dynamic service discovery: automatically populate Consul's catalog so that load balancers, proxies, and other consumers can discover services by name.
- Health-aware routing: leverage Docker and Consul health checks so that only healthy containers receive traffic.
- Zero-touch service lifecycle: no manual Consul API calls needed — just start or stop containers and the catalog updates itself.
- Gradual service renaming: use aliases to introduce new service names while keeping the old ones working during a migration period.
https://docs.astral.sh/uv/getting-started/installation/
uv syncuv run serviceregistrator --helpUnit tests:
uv run pytest tests/Integration tests (require Docker and pull a Consul image):
uv run pytest -m integration -vAll tests:
uv run pytest -m '' -vLint, format, and type checks:
uv run ruff check serviceregistrator tests
uv run ruff format --check serviceregistrator tests
uvx ty@0.0.29 check serviceregistratorManual end-to-end testing (see testing/README.md):
cd testing/dummyservice
./run_all.shdocker build . -t serviceregistratordocker run --rm serviceregistrator --helpUsage: serviceregistrator [OPTIONS]
Register docker containers as consul services
Options:
-i, --ip TEXT address to use for services without
SERVICE_IP [required]
-t, --tags TEXT comma-separated list of tags to append to
all registered services
-h, --consul-host TEXT consul agent host [default: 127.0.0.1]
-p, --consul-port INTEGER consul agent port [default: 8500]
-k, --dockersock TEXT path to docker socket [default:
/var/run/docker.sock]
-d, --delay INTEGER delay in seconds between reconnection
attempts [default: 1]
-s, --resync INTEGER delay between each forced services resync
[default: (disabled)]
-f, --logfile TEXT log file path
-l, --loglevel [CRITICAL|ERROR|WARNING|INFO|DEBUG]
log level [default: INFO]
-G, --debug output extra debug info
-R, --debug-requests log requests too (debug)
-P, --service-prefix TEXT string to prepend to all service names and
IDs (testing purpose)
--help Show this message and exit.
ServiceRegistrator discovers services by reading SERVICE_* environment
variables and Docker labels on each container. These variables control what gets
registered in Consul and how.
- A container must have explicitly published ports (
-por-P). For containers in host network mode, exposed ports (EXPOSE) are used. - A container must define a
SERVICE_NAME(orSERVICE_<port>_NAMEfor a specific port). Containers without a name are skipped — nothing is registered. - ServiceRegistrator reads all
SERVICE_*environment variables and labels, builds a service object for each named port, and registers it in Consul.
You can set these as either environment variables (--env) or Docker
labels (--label). Environment variables take precedence over labels when
both are set.
There are two forms:
SERVICE_<KEY>=<value>— applies to all ports on the container (or the single exposed port).SERVICE_<port>_<KEY>=<value>— applies only to the service on that internal (container) port. Port-specific values override the generic ones.
The recognized keys are:
| Key | Description | Example |
|---|---|---|
NAME |
(Required) Service name registered in Consul | SERVICE_NAME=postgres |
IP |
Override the service IP address | SERVICE_IP=10.0.0.5 |
TAGS |
Comma-separated list of Consul tags | SERVICE_TAGS=primary,db |
ALIAS |
Register an additional service name (see Service Alias) | SERVICE_ALIAS=pg-master |
Any other key is stored as a service attribute (key-value metadata):
SERVICE_REGION=us-east # sets attr "region" = "us-east"
SERVICE_80_WEIGHT=10 # sets attr "weight" = "10" on port 80Attributes are also used to configure Consul health checks.
Container authors can set defaults in their Dockerfile; operators can override
them at docker run time.
docker run -d \
--env "SERVICE_NAME=myapi" \
--env "SERVICE_TAGS=web,public" \
--env "SERVICE_80_CHECK_HTTP=/healthz" \
--env "SERVICE_80_CHECK_INTERVAL=15s" \
--publish "8080:80" \
myimageThis registers a service named myapi in Consul at the host's IP on port 8080,
with tags web and public, and an HTTP health check hitting /healthz every
15 seconds.
For a multi-port container, use port-specific names:
docker run -d \
--env "SERVICE_80_NAME=myapi" \
--env "SERVICE_443_NAME=myapi-ssl" \
--publish "8080:80" \
--publish "8443:443" \
myimageThe --ip command-line flag sets the default IP for all services. It can be
overridden per-container with SERVICE_IP or per-port with
SERVICE_<port>_IP.
You can register a service under an additional name using SERVICE_ALIAS or
SERVICE_<port>_ALIAS. This creates a second service in Consul with a
Consul alias health check
that mirrors the health of the original service.
This is useful for renaming services without breaking existing Consul template files. For example:
docker run -d \
--env "SERVICE_80_NAME=haproxy-postgres-primary" \
--env "SERVICE_80_ALIAS=postgres-master" \
--publish "5432:80" \
myimageThis registers two services in Consul:
haproxy-postgres-primary— the real service with its normal health checkpostgres-master— an alias that mirrors the health ofhaproxy-postgres-primary
Existing Consul template files using {{if service "postgres-master"}} will
continue to work while you migrate to the new naming convention.
The alias service inherits the same IP, port, and tags as the original service.
Its service ID has an :alias suffix appended.
Health checks are configured through service attributes (the SERVICE_*
variables with check-related keys). The following check types are supported:
HTTP / HTTPS check:
SERVICE_80_CHECK_HTTP=/health # path to check
SERVICE_80_CHECK_INTERVAL=15s # check interval (default: 10s)
SERVICE_80_CHECK_TIMEOUT=2s # request timeout
SERVICE_443_CHECK_HTTPS=/health # same for HTTPS
SERVICE_443_CHECK_TLS_SKIP_VERIFY=trueTCP check:
SERVICE_5432_CHECK_TCP=true
SERVICE_5432_CHECK_INTERVAL=10s
SERVICE_5432_CHECK_TIMEOUT=3sTTL check:
SERVICE_CHECK_TTL=30sScript check:
SERVICE_CHECK_SCRIPT=curl --silent --fail http://$SERVICE_IP:$SERVICE_PORT/healthDocker check:
SERVICE_CHECK_DOCKER=curl --silent --fail http://localhost/healthCommon check options:
| Key | Description |
|---|---|
CHECK_INTERVAL |
Time between checks (default: 10s) |
CHECK_TIMEOUT |
Check timeout |
CHECK_DEREGISTER |
Deregister after being critical for this duration |
CHECK_INITIAL_STATUS |
Initial check status (passing, warning, critical) |
The service ID is a cluster-wide unique identifier generated automatically:
<hostname>:<container-name>:<exposed-port>[:udp if udp]
This is mostly an implementation detail — you typically use service names, not IDs.
Docker hub: https://hub.docker.com/repository/docker/metabrainz/serviceregistrator
Image tags:
- latest: points to latest released version (vA.B.C)
- vA.B.C: released version
- edge: latest build
Images are automatically built and pushed using Git Workflow (in this repo).
docker run \
--detach \
--restart unless-stopped \
--name=serviceregistrator \
--net=host \
--volume=/var/run/docker.sock:/var/run/docker.sock \
metabrainz/serviceregistrator:latest \
--ip 127.0.0.1 \
--consul-port 8500 \
--consul-host localhostdocker run \
--detach \
--restart unless-stopped \
--name=serviceregistrator \
--add-host=host.docker.internal:host-gateway \
--volume=/var/run/docker.sock:/var/run/docker.sock \
metabrainz/serviceregistrator:latest \
--ip 127.0.0.1 \
--consul-port 8500 \
--consul-host host.docker.internalSee testing/dummyservice