A lightweight, zero-config development proxy for local Docker containers. Automatically routes traffic to your containers using custom domains with full HTTPS support.
- Automatic Docker Discovery - Detects containers via labels, no manual configuration needed
- Custom Local Domains - Use
.localhostor any custom TLD for your services - Automatic HTTPS - Generates trusted certificates on-the-fly via built-in CA
- DNS Server - Resolves custom domains without
/etc/hostsmodifications - TCP/SNI Routing - Route any TCP traffic including databases
- Hot Reload - Configuration changes apply without restart
# Install (macOS)
brew install munichmade/tap/devproxy
# Or build from source
git clone https://github.com/munichmade/devproxy
cd devproxy
make install
# Initial setup (creates CA, configures DNS resolver - requires sudo)
sudo devproxy setup
# Start the daemon
devproxy start
# Add labels to your container
docker run -d \
--label "devproxy.enable=true" \
--label "devproxy.host=myapp.localhost" \
nginx
# Visit https://myapp.localhostbrew install munichmade/tap/devproxy# Download the latest release
curl -L https://github.com/munichmade/devproxy/releases/latest/download/devproxy-linux-amd64 -o devproxy
chmod +x devproxy
sudo mv devproxy /usr/local/bin/git clone https://github.com/munichmade/devproxy
cd devproxy
make build
sudo make installRun the setup command to initialize devproxy:
sudo devproxy setupThis will:
- Create a local Certificate Authority (CA)
- Trust the CA in the system keychain
- Configure DNS resolver for
.localhostdomains - Create the configuration directory
# Start as daemon
devproxy start
# Stop daemon
devproxy stop
# Restart daemon
devproxy restart
# Check status
devproxy status
# View logs
devproxy logs -fAdd labels to your containers to enable automatic routing:
| Label | Description | Example |
|---|---|---|
devproxy.enable |
Enable routing for container | true |
devproxy.host |
Domain name(s) to route | myapp.localhost |
devproxy.port |
Container port to route to (default: 80) | 8080 |
devproxy.entrypoint |
TCP entrypoint name for non-HTTP services | postgres |
Route multiple domains to the same container port:
labels:
- "devproxy.enable=true"
- "devproxy.host=app.localhost,api.localhost"
- "devproxy.port=3000"Wildcard patterns are supported for matching subdomains:
labels:
- "devproxy.enable=true"
- "devproxy.host=*.myapp.localhost"
- "devproxy.port=3000"For containers exposing multiple services on different ports, use the services syntax:
labels:
- "devproxy.enable=true"
- "devproxy.services.web.host=app.localhost"
- "devproxy.services.web.port=3000"
- "devproxy.services.api.host=api.localhost"
- "devproxy.services.api.port=4000"
- "devproxy.services.db.host=db.localhost"
- "devproxy.services.db.port=5432"
- "devproxy.services.db.entrypoint=postgres"Each service name (e.g., web, api, db) can be any identifier. Each service supports:
host(required) - Domain name(s) to routeport(optional, default: 80) - Container portentrypoint(optional) - TCP entrypoint name for non-HTTP services
For non-HTTP services like databases, use the entrypoint label to route through a TCP entrypoint:
labels:
- "devproxy.enable=true"
- "devproxy.host=db.localhost"
- "devproxy.port=5432"
- "devproxy.entrypoint=postgres"The entrypoint name must match one defined in your config (e.g., postgres, mysql, mongo).
Note: For SSL mode "Preferred" PostgreSQL clients, devproxy automatically handles the PostgreSQL SSLRequest protocol to enable SNI-based routing.
# docker-compose.yml
services:
web:
image: nginx
labels:
- "devproxy.enable=true"
- "devproxy.host=web.localhost"
- "devproxy.port=80"
api:
image: node:20
labels:
- "devproxy.enable=true"
- "devproxy.host=api.localhost"
- "devproxy.port=3000"
db:
image: postgres:16
labels:
- "devproxy.enable=true"
- "devproxy.host=db.localhost"
- "devproxy.port=5432"
- "devproxy.entrypoint=postgres"The configuration file is located at ~/.config/devproxy/config.yaml.
# DNS server configuration
dns:
# Enable/disable the built-in DNS server
# Set to false if using external DNS (e.g., dnsmasq)
enabled: true
# Address and port to listen on
# Uses unprivileged port by default (resolver configured via setup)
listen: ":15353"
# Domains to handle (will resolve to 127.0.0.1)
# All subdomains are automatically included (e.g., *.localhost)
domains:
- localhost
- test
# Upstream DNS server for non-matching queries
upstream: "8.8.8.8:53"
# Entrypoints define the ports devproxy listens on
# Reserved names: "http" and "https" are handled specially
entrypoints:
# HTTP entrypoint (redirects to HTTPS by default)
http:
listen: ":80"
# HTTPS entrypoint (TLS termination with auto-generated certs)
https:
listen: ":443"
# TCP entrypoints for databases and other services
# The name is used in container labels: devproxy.entrypoint=postgres
# Note: Only postgres and mongo are included by default
postgres:
listen: ":15432" # Port devproxy listens on
target_port: 5432 # Default backend port (optional)
mongo:
listen: ":27017"
target_port: 27017
# Additional entrypoints can be added as needed:
# mysql:
# listen: ":13306"
# target_port: 3306
#
# redis:
# listen: ":16379"
# target_port: 6379
# Docker integration settings
docker:
# Enable/disable Docker container discovery
enabled: true
# Docker socket path
socket: "unix:///var/run/docker.sock"
# Logging configuration
logging:
# Log level: debug, info, warn, error
level: "info"
# Enable HTTP access logging
access_log: falseIf no configuration file exists, devproxy creates one with these defaults:
| Setting | Default Value |
|---|---|
dns.enabled |
true |
dns.listen |
:15353 |
dns.domains |
["localhost"] |
dns.upstream |
8.8.8.8:53 |
entrypoints.http.listen |
:80 |
entrypoints.https.listen |
:443 |
entrypoints.postgres.listen |
:15432 |
entrypoints.postgres.target_port |
5432 |
entrypoints.mongo.listen |
:27017 |
entrypoints.mongo.target_port |
27017 |
docker.enabled |
true |
docker.socket |
unix:///var/run/docker.sock |
logging.level |
info |
logging.access_log |
false |
| Platform | Config File | Data Directory |
|---|---|---|
| macOS | ~/.config/devproxy/config.yaml |
~/Library/Application Support/devproxy/ |
| Linux | ~/.config/devproxy/config.yaml |
~/.local/share/devproxy/ |
Data directory contains:
ca/- Root CA certificate and private keycerts/- Generated TLS certificatesdevproxy.log- Daemon log filedevproxy.pid- PID fileroutes.json- Active route registry
Environment variables XDG_CONFIG_HOME and XDG_DATA_HOME are respected.
Devproxy supports hot reloading of configuration changes. Changes are applied automatically when:
- The config file is modified (file watcher)
- Docker containers start/stop (automatic discovery)
Hot-reloadable settings (no restart required):
| Setting | Description |
|---|---|
logging.level |
Log level changes apply immediately |
dns.domains |
Add/remove handled domains |
dns.upstream |
Change upstream DNS server |
Settings requiring restart:
| Setting | Description |
|---|---|
dns.listen |
DNS server listen address/port |
entrypoints.*.listen |
Entrypoint listen addresses |
docker.label_prefix |
Docker label prefix |
docker.socket |
Docker socket path |
When a setting that requires restart is changed, devproxy logs a warning message indicating a restart is needed.
Check if the DNS server is running:
dig @127.0.0.1 myapp.localhostVerify resolver configuration:
# macOS
cat /etc/resolver/localhost
# Linux (systemd-resolved)
resolvectl statusRe-run the setup command:
sudo devproxy setupFor browsers, you may need to restart them after trusting the CA.
Ensure labels are correctly set:
docker inspect <container> | grep -A 20 LabelsCheck devproxy logs:
devproxy logs -fCheck what's using the ports:
sudo lsof -i :80
sudo lsof -i :443
sudo lsof -i :15353 # DNS serverdevproxy needs root privileges to:
- Bind to ports below 1024 (53, 80, 443)
- Modify DNS resolver configuration
- Trust CA in system keychain
Run with sudo or configure capabilities:
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/devproxy┌─────────────────────────────────────────────────────────────┐
│ devproxy │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────────┐ │
│ │ DNS │ │ HTTP │ │ HTTPS │ │ TCP Router │ │
│ │ :15353 │ │ :80 │ │ :443 │ │ :15432,:13306 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └───────┬───────┘ │
│ │ │ │ │ │
│ v v v v │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Route Manager │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Watcher │ │
│ │ (container discovery via labels) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Certificate Manager │ │
│ │ (on-demand cert generation) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
v
┌─────────────────┐
│ Docker │
│ Containers │
└─────────────────┘
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
make test - Run linter:
make lint - Commit:
git commit -m "Add my feature" - Push:
git push origin feature/my-feature - Open a Pull Request
# Clone
git clone https://github.com/munichmade/devproxy
cd devproxy
# Install dependencies
go mod download
# Build
make build
# Run tests
make test
# Run with race detector
make test-raceReleases are automated via GitHub Actions and GoReleaser.
-
Update
CHANGELOG.mdwith the new version -
Commit the changes:
git commit -am "chore: prepare release vX.Y.Z" -
Create and push a tag:
git tag vX.Y.Z git push origin main --tags
The release workflow will automatically:
- Build binaries for all platforms (darwin/linux × amd64/arm64)
- Create a GitHub Release with archives and checksums
- Update the Homebrew formula in
munichmade/homebrew-tap
MIT License - see LICENSE for details.