Guide for setting up a local development environment for roji.
- Docker & Docker Compose
- Go 1.25+ (for local builds without Docker)
- mkcert (optional - for custom certificates)
docker network create rojiCertificates are automatically generated on first startup. You can also use mkcert for custom certificates:
# Option 1: Auto-generation (default)
# Just start roji - certificates will be created automatically
# Set ROJI_AUTO_CERT=true in .env (default)
# Option 2: Using mkcert (optional)
# Install mkcert (first time only)
# macOS: brew install mkcert
# Linux: sudo apt install mkcert
# Install root CA to system (first time only)
mkcert -install
# Generate certificates
mkdir -p certs
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
"*.dev.localhost" dev.localhost localhost 127.0.0.1
# Disable auto-generation in .env
# ROJI_AUTO_CERT=false# Copy example env file
cp .env.example .env
# Edit .env to customize settings# Start with hot reload
docker compose up
# Or run in background
docker compose up -d
docker compose logs -fThe development server uses air for hot reload. Any changes to .go files will automatically trigger a rebuild.
mkdir -p test
cat > test/docker-compose.yml << 'EOF'
services:
web:
image: nginx:alpine
networks:
- roji
api:
image: nginx:alpine
labels:
- "roji.host=api.dev.localhost"
networks:
- roji
networks:
roji:
external: true
EOF
cd test && docker compose up -d# Check routes in logs
docker compose logs roji-dev
# Access via browser or curl
curl -k https://web.dev.localhost
curl -k https://api.dev.localhost
# View dashboard
open https://dev.localhost
# Check status
curl -k https://dev.localhost/_api/status | jq
# Check health
curl -k https://dev.localhost/_api/health | jq
# List routes via API
curl -k https://dev.localhost/_api/routes | jq# List routes (requires roji to be running)
docker compose exec roji-dev roji routes
# Show version
docker compose exec roji-dev roji version
# Get help
docker compose exec roji-dev roji --helpThe development container mounts the source code and uses air for automatic rebuilding:
# Watch logs while developing
docker compose logs -f
# Rebuild container if Dockerfile or go.mod changes
docker compose up --build# Run all tests
go test ./...
# Run tests with coverage
go test -v -race -coverprofile=coverage.out ./...
# View coverage report
go tool cover -html=coverage.out
# Run tests in container
docker compose exec roji-dev go test ./...
# Run specific package tests
go test ./proxy/...
go test ./docker/...Before submitting a PR, ensure:
- All tests pass:
go test ./... - Code is formatted:
go fmt ./... - No linting errors:
go vet ./... - New features have tests
- Documentation is updated
# Build production image
docker build --target production -t roji:latest .
# Run production image
docker run -d \
-p 80:80 -p 443:443 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v ./certs:/certs \
--network roji \
roji:latestConfigure via .env file (gitignored) or directly in shell:
| Variable | Description | Default |
|---|---|---|
ROJI_NETWORK |
Docker network to watch | roji |
ROJI_DOMAIN |
Base domain | dev.localhost |
ROJI_HTTP_PORT |
HTTP port | 80 |
ROJI_HTTPS_PORT |
HTTPS port | 443 |
ROJI_CERTS_DIR |
Certificate directory | /certs |
ROJI_DASHBOARD |
Dashboard hostname | roji.{domain} |
ROJI_LOG_LEVEL |
Log level (debug/info/warn/error) | debug |
roji/
├── cmd/roji/
│ ├── main.go # Entry point
│ └── cmd/ # Cobra commands
│ ├── root.go # Root command (server)
│ ├── server.go # Server implementation
│ ├── routes.go # Routes list command
│ ├── version.go # Version command
│ ├── health.go # Health check command
│ ├── config.go # Config management command
│ ├── doctor.go # Environment diagnostics command
│ └── ca.go # CA certificate management command
├── docker/
│ ├── client.go # Docker API wrapper
│ └── watcher.go # Events watcher
├── proxy/
│ ├── handler.go # ReverseProxy implementation
│ ├── router.go # Routing + SSE Pub/Sub
│ └── templates/ # HTML/CSS/JS (embed.FS)
├── certgen/
│ ├── generator.go # TLS certificate generator
│ ├── installer.go # CAInstaller interface
│ ├── installer_darwin.go # macOS Keychain
│ ├── installer_linux.go # Linux (Debian/RHEL)
│ ├── installer_windows.go # Windows certutil
│ └── installer_wsl.go # WSL→Windows
├── config/
│ ├── labels.go # Label parser
│ ├── paths.go # XDG path utilities
│ └── settings.go # Configuration loading
├── doctor/
│ ├── check.go # Doctor interface
│ └── checks/ # Individual checks
├── project/
│ └── store.go # Project history storage
├── test/ # Integration/E2E tests
├── Dockerfile # Multi-stage build (development/testing)
├── docker-compose.yml # Development and testing
├── docker-compose.dev.yml # Development with Air hot reload
├── Makefile # Build shortcuts
├── .air.toml # Hot reload configuration
├── .env.example # Environment template
├── go.mod
└── go.sum
| OS | Solution |
|---|---|
| macOS | Automatically resolves to 127.0.0.1 |
| Linux | Add to /etc/hosts or configure systemd-resolved |
| Windows (WSL2) | Add to Windows hosts file |
# Linux: add to /etc/hosts
echo "127.0.0.1 web.dev.localhost api.dev.localhost dev.localhost" | sudo tee -a /etc/hostsEdit .env to use different ports:
ROJI_HTTP_PORT=8080
ROJI_HTTPS_PORT=8443-
Check network connection:
docker network inspect roji
-
Check container port configuration:
docker inspect <container> | jq '.[0].Config.ExposedPorts'
-
Ensure container doesn't have
roji.self=truelabel
# Reinstall root CA
mkcert -install
# Regenerate certificates
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
"*.dev.localhost" dev.localhost localhost 127.0.0.1
# Restart browser# Check air logs
docker compose logs -f
# Rebuild container
docker compose up --build