A simple reverse proxy for local development environments. Automatically discovers Docker Compose services and provides HTTPS access via *.dev.localhost.
"Use the highway (Traefik) for production, take the back alley (roji) for development"
- Auto-discovery: Automatically detects and routes containers on the shared network
- TLS Support: Auto-generates certificates (no mkcert required) or use your own
- Label-based Configuration: Customize hostnames and ports via container labels
- Dynamic Updates: Automatically tracks container start/stop events
- Dashboard: View current routes in your browser
- Simple: Minimal implementation focused on local development
Install and start roji with a single command:
curl -fsSL https://raw.githubusercontent.com/kan/roji/main/install.sh | bashThis will:
- Check Docker and Docker Compose prerequisites
- Create the
rojinetwork - Install roji to
~/.roji(customize withROJI_INSTALL_DIR) - Start roji with default settings
- Generate TLS certificates automatically
- Display CA certificate installation instructions
Custom installation directory:
curl -fsSL https://raw.githubusercontent.com/kan/roji/main/install.sh | ROJI_INSTALL_DIR=/opt/roji bashIf you prefer manual setup:
docker network create roji# Copy the example compose file
cp examples/docker-compose.yml docker-compose.yml
# Start roji
docker compose up -dCertificates are automatically generated on first startup. See TLS Certificates for how to trust them.
# your-app/docker-compose.yml
services:
myapp:
image: your-app
expose:
- "3000"
networks:
- roji
networks:
roji:
external: trueYour app is now accessible at https://myapp.dev.localhost!
roji automatically generates TLS certificates on first startup:
certs/
├── ca.crt # CA certificate (Windows)
├── ca.pem # CA certificate (macOS/Linux)
├── ca-key.pem # CA private key
├── cert.pem # Server certificate
└── key.pem # Server private key
To trust HTTPS connections, install the CA certificate in your OS/browser:
- Double-click
certs/ca.crt - Click "Install Certificate"
- Select "Local Machine" (requires admin) or "Current User"
- Select "Place all certificates in the following store"
- Click "Browse" → Select "Trusted Root Certification Authorities"
- Click "Next" → "Finish"
- Restart your browser
# Add to system keychain (requires password)
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certs/ca.pem
# Or open in Keychain Access and set "Always Trust"
open certs/ca.pem# Install certutil if needed
# Debian/Ubuntu: sudo apt install libnss3-tools
# Fedora: sudo dnf install nss-tools
# Add to Chrome/Chromium certificate store
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "roji CA" -i certs/ca.pemFirefox uses its own certificate store:
- Open Firefox → Settings → Privacy & Security
- Scroll to "Certificates" → Click "View Certificates"
- Go to "Authorities" tab → Click "Import"
- Select
certs/ca.pem(orca.crton Windows) - Check "Trust this CA to identify websites"
- Click OK
If you prefer mkcert, generate certificates before starting roji:
mkcert -install
mkdir -p certs
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
"*.dev.localhost" "*.yourproject.localhost" localhost 127.0.0.1roji will use existing certificates and skip auto-generation.
- Detects containers connected to the
rojinetwork - Uses the
EXPOSEd port (first one if multiple) - Generates hostname as
{service}.{domain}from the service name
| Label | Description | Default |
|---|---|---|
roji.host |
Custom hostname | {service}.dev.localhost |
roji.port |
Target port | First EXPOSE'd port |
roji.path |
Path prefix | none |
services:
# Custom hostname
api:
image: my-api
labels:
- "roji.host=api.dev.localhost"
networks:
- roji
# Port specification (when multiple ports are exposed)
app:
image: my-app
expose:
- "3000"
- "9229"
labels:
- "roji.port=3000"
networks:
- roji
# Path-based routing
# https://myapp.dev.localhost/api/* -> this service
api-service:
image: my-api
labels:
- "roji.host=myapp.dev.localhost"
- "roji.path=/api"
networks:
- roji| Variable | Description | Default |
|---|---|---|
ROJI_NETWORK |
Docker network to watch | roji |
ROJI_DOMAIN |
Base domain | dev.localhost |
ROJI_CERTS_DIR |
Certificate directory | /certs |
ROJI_DASHBOARD |
Dashboard hostname | {domain} |
ROJI_LOG_LEVEL |
Log level | info |
ROJI_AUTO_CERT |
Auto-generate certificates | true |
environment:
- ROJI_DOMAIN=dev.localhost # Use *.dev.localhost
- ROJI_DASHBOARD=dev.localhostAccess https://dev.localhost (or your custom configured host) to view a list of currently registered routes.
roji provides health check endpoints for monitoring and container orchestration:
/_api/health- JSON health status (consistent with API pattern)/healthz- Kubernetes/Docker standard health check
Both endpoints return the same response:
{
"status": "healthy",
"routes": 3
}Docker health check: Automatically configured in the production image (checks every 30 seconds).
roji provides a comprehensive status endpoint at /_api/status that shows the current state of the proxy:
{
"version": "0.1.0",
"uptime_seconds": 3600,
"certificates": {
"auto_generated": true,
"directory": "/certs",
"ca": {
"exists": true,
"valid_until": "2035-01-15T12:00:00Z",
"days_remaining": 3650,
"subject": "CN=roji CA,O=roji Dev CA"
},
"server": {
"exists": true,
"valid_until": "2026-01-15T12:00:00Z",
"days_remaining": 365,
"subject": "CN=*.dev.localhost",
"dns_names": ["*.dev.localhost", "dev.localhost", "localhost"]
}
},
"docker": {
"connected": true,
"network": "roji"
},
"proxy": {
"routes_count": 3,
"dashboard_host": "dev.localhost",
"base_domain": "localhost",
"http_port": 80,
"https_port": 443
},
"health": "healthy"
}The health field indicates the overall system health:
healthy- All systems operationaldegraded- Certificates expiring within 30 days or missingunhealthy- Docker connection lost
macOS: .localhost automatically resolves to 127.0.0.1.
Linux: Add to /etc/hosts or configure dnsmasq:
echo "127.0.0.1 myapp.dev.localhost dev.localhost" | sudo tee -a /etc/hostsOr use *.lvh.me (a public domain that always resolves to 127.0.0.1)
-
Verify the container is connected to the
rojinetwork:docker network inspect roji
-
Check if the port is exposed:
docker inspect <container> | jq '.[0].Config.ExposedPorts'
The CA certificate is not trusted. See TLS Certificates for installation instructions.
Important: On Windows, make sure to install the certificate in the "Trusted Root Certification Authorities" store, not the default store.
After installing, restart your browser completely (close all windows).
roji means "back alley" or "narrow lane" in Japanese. The concept is to use the highway (Traefik) for production and casually take the back alley (roji) for local development.
MIT