Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🅿️ proxypark

Local reverse-proxy & DNS manager for macOS. Zero port conflicts, parallel development, trusted HTTPS — all from a single CLI.

proxypark replaces the infrastructure layer of tools like Laravel Valet with a framework-agnostic setup built on Traefik, dnsmasq, and mkcert.

Browser → https://myapp.test
              │
         ┌────┴─────┐
         │  Traefik  │  :443 (only port listener)
         └────┬──────┘
              │
         File-provider
         (~/.proxypark/dynamic/)
              │
    ┌─────────┼──────────────┐
    │         │              │
    ▼         ▼              ▼
 park link  park link:docker  park valet enable
 localhost  container:port    Valet catchall
 (exposed)  (no exposed       on localhost:8080
             ports needed)

Why?

If you run Docker-based projects alongside native services (PHP, Node, etc.), you've likely hit port conflicts — every project wants :80 and :3000. proxypark solves this by routing everything through Traefik using hostnames instead of ports.

  • No port conflicts — Everything routes by hostname, not port.
  • Parallel development — Run 5+ projects at once, each on its own *.test domain.
  • Trusted HTTPS — Per-domain mkcert certificates, accepted by all browsers including Firefox.
  • Mix Docker & native — Services registered via park link, Valet as optional fallback.
  • Framework-agnostic — Works with any stack. Laravel, Nuxt, Rails, Go, whatever.
  • No Docker socket needed — Uses Traefik's file-provider exclusively. No socket permission headaches.

Quick start

# Clone and symlink
git clone git@github.com:McGo/proxypark.git ~/proxypark
ln -sf ~/proxypark/park /usr/local/bin/park

# Run setup (installs mkcert, dnsmasq, starts Traefik)
park install

# Link a local service
park link myapp 3000
# → https://myapp.test

# Optional: route unmatched domains to Valet
park valet enable

Commands

Infrastructure

Command Description
park install First-time setup
park uninstall Remove everything
park start Start Traefik
park stop Stop Traefik
park restart Restart Traefik
park status Health check
park doctor [--fix] Diagnose and auto-fix common issues
park trust (Re-)install mkcert CA (system + Firefox)
park certs Regenerate certificates from current links
park logs Tail Traefik logs
park dashboard Open Traefik dashboard

Projects

Use dot notation for subdomains: api.myapphttps://api.myapp.test

Command Description
park link <name> <port> Link local service → https://<name>.test
park link:docker <name> <container>[:<port>] Link Docker container (no exposed ports)
park unlink <name> Remove link
park list Show all routes
park open <name> Open project in browser
park scaffold <name> Generate docker-compose.yml template

Valet integration

Command Description
park valet enable [port] Route unmatched *.test to Valet (default: 8080)
park valet disable Remove Valet catchall

How it works

File-provider architecture

proxypark uses Traefik's file-provider exclusively. No Docker socket is mounted — this avoids permission issues with Docker Desktop, OrbStack, Colima, etc.

Each park link creates a small YAML config in ~/.proxypark/dynamic/. Traefik watches this directory and picks up changes instantly — no restart needed.

Per-domain certificates

.test is on the Public Suffix List, which means browsers reject wildcard certificates (*.test) just like they would reject *.com. proxypark solves this by generating explicit per-domain certificates via mkcert.

Every park link and park unlink automatically regenerates the certificate bundle to include all currently linked domains. Traefik picks up the new certs via its file-watcher.

DNS

proxypark installs its own dnsmasq config (independent of Valet) that resolves all *.test domains to 127.0.0.1. A macOS resolver entry at /etc/resolver/test tells the OS to use the local dnsmasq for .test lookups.

Valet coexistence

If you still have Valet projects, park valet enable creates a low-priority catchall that routes any *.test domain not matched by an explicit link to Valet on port 8080. Explicit park link entries always take priority.

# Shift Valet to port 8080
valet stop && valet port 8080 && valet start

# Enable catchall in proxypark
park valet enable

Using with Docker projects

Option A: With exposed ports (park link)

If your containers expose ports to the host, use park link like any local service:

park link myapp 3000
# → https://myapp.test

Option B: Without exposed ports (park link:docker)

park link:docker connects containers directly via Docker's proxy network — no port mapping needed:

park link:docker myapp myapp-container
# → https://myapp.test → myapp-container:80

park link:docker myapp myapp-container:3000
# → https://myapp.test → myapp-container:3000

When called without arguments in a directory with a docker-compose.yml, it shows available services:

park link:docker
# SERVICE     CONTAINER        STATE
# api         myapp-api        running
# app         myapp-app        stopped

After linking, park link:docker checks your docker-compose.yml and offers to add container_name and the proxy network if missing — so containers stay reachable after docker compose down/up.

Subdomains (dot notation)

Use dot notation for subdomains with both link and link:docker:

park link api.myapp 8001
# → https://api.myapp.test

park link:docker api.myapp api-container:3000
# → https://api.myapp.test → api-container:3000

Use park scaffold <name> to generate a boilerplate docker-compose.yml.

Integrating with an existing local dev stack

If you already have a docker-compose.yml with shared services (Postgres, Redis, etc.), you can add Traefik as a service. Add this to your existing compose file:

services:
  traefik:
    image: traefik:v3.2
    container_name: proxypark
    restart: unless-stopped
    command:
      - --api.insecure=true
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls=true
      - --providers.file.directory=/etc/traefik/dynamic
      - --providers.file.watch=true
      - --log.level=INFO
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - ~/.proxypark/dynamic:/etc/traefik/dynamic:ro
      - ~/.proxypark/certs:/etc/traefik/certs:ro
    networks:
      - proxy
    extra_hosts:
      - "host.docker.internal:host-gateway"

networks:
  proxy:
    name: proxy
    external: true

Then use park link to route services with web UIs:

park link mailpit 8025        # https://mailpit.test
park link meilisearch 7700    # https://meilisearch.test

Migrating from Valet

  1. Run park install
  2. Shift Valet to port 8080: valet stop && valet port 8080 && valet start
  3. Enable Valet catchall: park valet enable
  4. For each project you want explicit HTTPS:
    • park link projectname <port>
  5. Once all projects are migrated: valet stop && valet uninstall && park valet disable

Configuration

All runtime data lives in ~/.proxypark/:

~/.proxypark/
├── docker-compose.yml          # Traefik container (standalone mode)
├── certs/
│   ├── wildcard.test.pem       # Per-domain cert bundle
│   └── wildcard.test-key.pem
└── dynamic/                    # Traefik file-provider configs
    ├── tls.yml                 # TLS certificate reference
    ├── valet.yml               # Valet catchall (optional)
    ├── link-myapp.yml          # park link
    ├── link-api-myapp.yml      # park link (subdomain)
    └── link-docker-myapp.yml   # park link:docker

Customize via environment variables:

export PROXYPARK_TLD=dev        # Use .dev instead of .test
export PROXYPARK_DATA_DIR=...   # Different data path

Troubleshooting

Port 80 already in use:

lsof -i :80   # Find the culprit
# Common: nginx, apache, Valet

DNS not resolving:

dscacheutil -flushcache
dig myapp.test @127.0.0.1    # Should return 127.0.0.1
park status                   # Full health check

HTTPS certificate not trusted:

park trust        # Reinstall CA (system + Firefox via nss)
# Then restart your browser

Certificate doesn't cover a domain:

park certs        # Regenerate from all current links

Requirements

Roadmap

  • park doctor – Automated diagnostics and fixes
  • Zsh/Bash completions
  • Homebrew tap (brew install McGo/tap/proxypark)
  • park serve – Start a simple PHP/Node server and auto-link

License

MIT — see LICENSE.

About

Local reverse-proxy and DNS manager for macOS. One CLI gives every project its own .test domain with trusted HTTPS, so Docker containers and native services run side by side without port conflicts. Built on Traefik, dnsmasq and mkcert — framework-agnostic, with a Valet catchall for existing setups.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages