Integrating applications with localghost¶
An application joins the proxy's external Docker network and describes its routes using Traefik labels. The proxy and application remain separate Compose projects.
Prerequisites¶
Start the proxy before the first application. This creates the external
localghost network:
Compose automatically derives the project name from the checkout directory and
makes it available to labels as ${COMPOSE_PROJECT_NAME}. Nothing needs to be
configured when that name is unique and contains only lowercase letters,
digits, and hyphens.
Override it only when the derived name is unsafe, duplicated, or not the
hostname you want. For example, set it in .env:
You can instead use the COMPOSE_PROJECT_NAME environment variable or
Compose's project-name option.
Primary service¶
This example publishes a service listening on container port 8000 at
http://my-project.localhost:
services:
web:
networks:
- default
- localghost
labels:
- "traefik.enable=true"
- "traefik.docker.network=localghost"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web.entrypoints=web"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web.rule=Host(`${COMPOSE_PROJECT_NAME}.localhost`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web.service=${COMPOSE_PROJECT_NAME}-web"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web-secure.entrypoints=websecure"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${COMPOSE_PROJECT_NAME}.localhost`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web-secure.service=${COMPOSE_PROJECT_NAME}-web"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-web-secure.tls=true"
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-web.loadbalancer.server.port=8000"
networks:
localghost:
external: true
The application process must listen on 0.0.0.0:8000, not 127.0.0.1:8000,
inside its container. The backend port label is a container port; it is not a
host-published port.
Do not add ports for a service used only through the proxy. Keeping the
service off host ports avoids collisions and keeps the proxy as the single HTTP
entrypoint. The service can remain on its default network for private
application dependencies.
The router's explicit service label avoids implicit association. The explicit
load-balancer port remains deterministic if the image or Compose definition
later exposes another port.
Secondary services¶
Secondary web interfaces follow <service>.<project>.localhost. For a Mailpit
service listening on container port 8025:
services:
mailpit:
image: axllent/mailpit:v1.27.7 # Choose and review the version used by your project.
networks:
- default
- localghost
labels:
- "traefik.enable=true"
- "traefik.docker.network=localghost"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit.entrypoints=web"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit.rule=Host(`mailpit.${COMPOSE_PROJECT_NAME}.localhost`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit.service=${COMPOSE_PROJECT_NAME}-mailpit"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit-secure.entrypoints=websecure"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit-secure.rule=Host(`mailpit.${COMPOSE_PROJECT_NAME}.localhost`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit-secure.service=${COMPOSE_PROJECT_NAME}-mailpit"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-mailpit-secure.tls=true"
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-mailpit.loadbalancer.server.port=8025"
networks:
localghost:
external: true
For a checkout named my-project, the URL is
http://mailpit.my-project.localhost.
Repeat the pattern for other browser-facing tools. The service segment and the router/service identifiers must be distinct within the project.
Optional HTTPS¶
Each example defines two routers for the same hostname and backend service. The
ordinary router uses the always-available web entrypoint. The -secure router
uses websecure with TLS and becomes active after localghost trust enables
the proxy's HTTPS configuration. Keeping both routers means HTTP continues to
work after HTTPS is enabled.
The generator adds both routers automatically. For hand-written integration,
include all four -secure labels shown above: entrypoint, rule, service, and
tls=true. A secure router without an explicit service label can be associated
with the wrong backend when a container defines multiple routers or services.
Install mkcert, then enable and inspect trust with:
Use https://<project>.localhost for the primary service and
https://<service>.<project>.localhost for secondary services. HTTP remains
available at the corresponding http:// URLs. See
Operating localghost for trust-store,
port, and removal details.
Multiple checkouts¶
Two checkouts of the same repository can use the same Compose configuration:
checkout primary URL
../my-project http://my-project.localhost
../my-project-review-123 http://my-project-review-123.localhost
Compose project names also isolate container and default-network names. Stopping one checkout leaves the other checkout and shared proxy running. Override a name only if two checkout directories have the same basename or a basename is not DNS-safe.
Framework configuration¶
Traefik routing does not bypass application security checks. Configure generated hostnames in trusted-host, origin, CORS, callback URL, and cookie settings as required by the framework.
For Django, a checkout named my-project normally needs:
When using HTTPS, allow its origin instead, or allow both origins when the application is intentionally used over both schemes:
When the proxy uses a non-default port, the browser origin includes it:
Cookie domain settings often work best when left host-only in local development. OAuth and other external callbacks must use the generated URL expected by the browser.
Failure behavior¶
Because localghost is declared external, application startup fails if
the proxy network has never been created. This is intentional: the application
must not silently create a private network with the same name. Start the proxy,
then rerun docker compose up for the application.
If the network exists but Traefik is stopped, application containers can start
and communicate on their other networks, but .localhost routes remain
unavailable until the proxy starts again.
Unlabelled containers and containers without traefik.enable=true receive no
route even when attached to the shared network.