Lightweight local development domain router with ASGI middleware support
Project description
🌐 Devhost
Secure, flexible local domain routing for developers.
Devhost allows you to map subdomains of a base domain (default: localhost, e.g. myapp.localhost) to local app ports, with optional HTTPS and wildcard routing via Caddy and a Python backend.
Features
- Map domains like
app.localhost→localhost:1234 - Map domains to remote IPs (e.g.
rpi.localhost→192.168.1.100:8080) - HTTPS support via Caddy's internal CA (use
--https) - Add/remove routes using a single CLI command
- Wildcard reverse proxy using Python (FastAPI)
- Custom base domain (e.g.
hello.flask) - Supports macOS, Linux, and Windows (native PowerShell shim)
Benefits for Devs
- No need to remember localhost:PORT combos
- Clean and memorable dev URLs
- HTTP by default; HTTPS available with
--https - Works with any language/framework running locally
Quickstart
1. Clone the project
git clone https://github.com/Patoruzuy/devhost.git
cd devhost
python install.py --linux
devhost add hello 3000
devhost list
devhost remove hello
Visit hello.localhost in your browser.
Note: the devhost CLI is implemented in Python (cross-platform).
Installation Options
The installer supports various flags to customize the setup process:
| Flag | Description | Platforms |
|---|---|---|
--yes |
Accept all prompts automatically (non-interactive mode) | All |
--dry-run |
Show what would be done without making any changes | All |
--domain <name> |
Set custom base domain (default: localhost) | All |
--start-dns |
Automatically start DNS service (dnsmasq) | macOS, Linux |
--install-completions |
Install shell completions for bash/zsh | macOS, Linux |
--caddy |
Install and configure Caddy web server | Windows |
--clean |
Remove existing installation before reinstalling | Windows |
Cross-platform installer (uses the Python CLI):
python install.py --linux
macOS example:
python install.py --macos --yes --start-dns --install-completions
Windows example (PowerShell):
python .\install.py --windows --caddy
To change the base domain (for example, hello.flask), set it once and re-run your installer to update DNS/resolvers:
devhost domain flask
python install.py --domain flask
Run the router locally (development):
cd router
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --host 127.0.0.1 --port 5555 --reload
Run the router in Docker (quick):
docker compose up --build -d
# then open http://127.0.0.1:5555 with Host header set to <name>.localhost
docker-compose.yml mounts the repo devhost.json into the container so edits take effect immediately.
Notes & safety
install.pyhandles Linux/macOS/Windows and will prompt you about DNS changes. Review DNS/resolver changes before applying them on systems usingsystemd-resolved.- We now generate the project
caddy/Caddyfile, the user~/.config/caddy/Caddyfile, and (if present)/etc/caddy/Caddyfileto keep system Caddy installs in sync. - The router loads
devhost.jsonper request so CLI changes take effect immediately without restarting the router.
Quick Commands
devhost add <name> <port|host:port>— add a mapping (e.g.devhost add hello 3000).devhost add <name> <ip:port>— add remote IP mapping (e.g.devhost add rpi 192.168.1.100:8080).devhost add <name> --http <port|host:port>— force HTTP when opening the dev URL.devhost add <name> --https <port|host:port>— force HTTPS when opening the dev URL.devhost remove <name>— remove a mapping.devhost list— show active mappings.devhost list --json— show mappings as JSON.devhost url <name>— print the URL and press Ctrl+O to open it in the browser.devhost open <name>— open the URL in the default browser.devhost validate— quick health checks (config JSON, router health, DNS).devhost export caddy— print the generated Caddyfile to stdout.devhost edit— opendevhost.jsonin$EDITOR(fallback:nano/vi).devhost resolve <name>— show DNS resolution and port reachability for a mapping.devhost doctor— deeper diagnostics (dnsmasq/systemd-resolved/Caddy).devhost doctor --windows— Windows-specific diagnostics (Caddy, port 80, hosts).devhost doctor --windows --fix— attempt Windows fixes (hosts sync + free port 80 + start Caddy).devhost info— show all commands and usage.devhost status --json— print router status as JSON (running, pid, health).devhost domain [name]— show or set the base domain (default:localhost).devhost hosts sync— re-apply hosts entries for all mappings on Windows (admin).devhost hosts clear— remove all devhost entries from the Windows hosts file (admin).devhost caddy start|stop|restart|status— manage Caddy on Windows.devhost fix-http— convert allhttps://mappings tohttp://and regenerate Caddyfile.
Remote IP Support
Devhost supports mapping domains to devices on your local network, not just localhost ports. This is perfect for:
- Raspberry Pi projects
- IoT devices
- Other computers on your network
- Docker containers with bridge networking
Examples
Raspberry Pi running a web server:
devhost add rpi 192.168.1.100:8080
# Access at http://rpi.localhost
Network attached storage (NAS) web interface:
devhost add nas 192.168.1.50:5000
devhost open nas # Opens http://nas.localhost
Remote development server:
devhost add staging 10.0.0.25:3000
# Access at http://staging.localhost
Docker container with bridge network:
devhost add docker 172.17.0.2:8080
Configuration Format
Remote IPs can be added in several formats:
192.168.1.100:8080- Full IP with port10.0.0.25:3000- Any valid IPv4 address- Combined with other targets in
devhost.json:
{
"hello": 3000,
"api": 8000,
"rpi": "192.168.1.100:8080",
"nas": "192.168.1.50:5000"
}
Notes
- The remote device must be reachable from your machine
- Ensure firewalls allow traffic to the target port
- IP addresses are validated when adding routes
- Use
devhost resolve <name>to test connectivity
Configuration
The project uses a devhost.json file (project root) with a simple mapping of names to ports. Example:
{
"hello": 3000,
"api": 8000
}
This file is created/updated by the CLI and is meant to be local (it’s gitignored). The router reads DEVHOST_CONFIG if set; otherwise it looks for the project root devhost.json (even when run from router/). The base domain comes from DEVHOST_DOMAIN or .devhost/domain (default: localhost).
Router endpoints
GET /health— liveness + route count + uptime.GET /metrics— basic request metrics (totals, per-status, per-subdomain).GET /routes— current routes with parsed targets.GET /mappings— current routes with basic TCP health checks.
Logging
DEVHOST_LOG_LEVELcontrols router log verbosity (default:INFO).DEVHOST_LOG_FILEwrites logs to a file in addition to stdout.DEVHOST_LOG_REQUESTS=1enables per-request logging.
Quick test (curl)
Run the router (locally or via Docker) and test with curl by setting the Host header:
curl -H "Host: hello.localhost" http://127.0.0.1:5555/
Regenerating Caddyfile
The devhost CLI writes both the project caddy/Caddyfile (generated, gitignored) and, when present, the user ~/.config/caddy/Caddyfile. Inspect generated files before reloading system Caddy and, when appropriate, reload the service:
# inspect
less caddy/Caddyfile
# if using system Caddy (Linux)
sudo systemctl reload caddy
Troubleshooting
General Issues
- Check mappings:
devhost list- Verify your routes are configured correctly. - Router health:
curl http://127.0.0.1:5555/healthshould return{ "status": "ok" }. - Check router logs: Look for request IDs in logs to trace specific requests (X-Request-ID header).
- Validate setup:
devhost validate- Quick health checks for config, router, and DNS. - Deep diagnostics:
devhost doctor- Comprehensive system diagnostics.
DNS & Domain Issues
- Linux DNS issues: Check
systemd-resolvedand/etc/resolv.conffor unintended changes. - macOS DNS issues: Verify
/etc/resolver/<domain>file exists and containsnameserver 127.0.0.1. - Windows DNS issues: Wildcard DNS requires a local resolver like Acrylic DNS. Alternatively, use
devhost hosts syncto add individual entries to hosts file. - Domain resolution:
devhost resolve <name>- Show DNS resolution and port reachability.
Windows-Specific Issues
Hosts File Management
When to use hosts file (Windows only):
- You don't have a wildcard DNS resolver installed (like Acrylic DNS)
- You want individual domain entries instead of wildcard DNS
- Testing specific routes without full DNS setup
Admin elevation required:
devhost hosts sync- Adds/updates all routes inC:\Windows\System32\drivers\etc\hosts(requires admin)devhost hosts clear- Removes all devhost entries from hosts file (requires admin)
Admin NOT required:
devhost add/remove/list- Regular route managementdevhost start/stop/status- Router process managementdevhost validate- Health checks
The hosts file commands modify system files and therefore require administrator privileges. The CLI will automatically attempt to relaunch with elevation if needed.
Other Windows Issues
- Port 80 conflicts: Run
devhost doctor --windowsto check what's using port 80. - Port 80 auto-fix:
devhost doctor --windows --fix- Attempts to free port 80 and start Caddy. - Caddy not running:
devhost caddy status- Check Caddy status. - Start Caddy:
devhost caddy start- Start Caddy web server. - Windows diagnostics:
devhost doctor --windows- Windows-specific checks.
HTTPS & Certificate Issues
- Browser forcing HTTPS: Clear HSTS settings for the domain or change base domain (
devhost domain devhost2). - Caddy not running: Ensure Caddy is running if you depend on system TLS (
systemctl status caddyon Linux). - Convert to HTTP:
devhost fix-http- Convert all HTTPS mappings to HTTP.
Remote IP Issues
- Remote device unreachable: Verify the remote IP/port is accessible from your machine (
curl http://192.168.1.100:8080). - Network firewall: Ensure firewall rules allow traffic to the remote device.
- Wrong IP address: Check the device's current IP (
devhost listto see configured IPs).
Platform notes
install.pytargets Linux/macOS/Windows; it readsDEVHOST_DOMAINor.devhost/domainto configure DNS for the base domain.- On Windows, run the installer from an elevated PowerShell if you want hosts entries updated automatically, or use a local DNS resolver (Acrylic) for wildcard domains.
Release notes
See CHANGELOG.md for the v1.0.0 release notes.
macOS installer
Run the Python installer to generate the LaunchAgent plist (from router/devhost-router.plist.tmpl), create /etc/resolver/<domain>, and optionally start dnsmasq via Homebrew:
# dry-run (print actions)
python install.py --macos --dry-run
# run interactively (will prompt for username and uvicorn path)
python install.py --macos
Non-interactive example (accept all prompts and start dnsmasq if available):
python install.py --macos --yes --start-dns
To use a custom base domain on macOS:
devhost domain flask
python install.py --macos --domain flask
Windows installer
Run the Python installer from PowerShell to prepare the venv, router deps, and initial config:
python .\install.py --windows --caddy
python .\devhost add hello 8000
To clean and reinstall:
python .\install.py --windows --clean
If you want a shortcut in PowerShell without typing python, use:
.\devhost.ps1 add hello 8000
.\devhost.ps1 start
Note: the router requires a Host header. Don’t browse http://127.0.0.1:5555 directly — use devhost open <name> or:
curl -H "Host: hello.localhost" http://127.0.0.1:5555/
Tip (Windows): if your app only listens on IPv4, Devhost uses `127.0.0.1` for numeric ports to avoid IPv6 `::1` connection errors.
Tip: On Windows, devhost.ps1 start will try to start Caddy (if installed) before starting the router.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file devhost-2.1.1.tar.gz.
File metadata
- Download URL: devhost-2.1.1.tar.gz
- Upload date:
- Size: 30.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
967266873581425f26c07b5f93a61e48aff420615dc911cd08f156b92d25f54c
|
|
| MD5 |
cd3da9c5fec0fa2d6f12388b6236bc80
|
|
| BLAKE2b-256 |
f425da0ec8cb26edd57f64874fbba5c3e5d0ff11972ddd5de336c4d91f2ddd22
|
Provenance
The following attestation bundles were made for devhost-2.1.1.tar.gz:
Publisher:
publish.yml on Patoruzuy/Devhost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
devhost-2.1.1.tar.gz -
Subject digest:
967266873581425f26c07b5f93a61e48aff420615dc911cd08f156b92d25f54c - Sigstore transparency entry: 894694106
- Sigstore integration time:
-
Permalink:
Patoruzuy/Devhost@8e84871da292c826b0c5f844044095c3c972fa9d -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/Patoruzuy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8e84871da292c826b0c5f844044095c3c972fa9d -
Trigger Event:
push
-
Statement type:
File details
Details for the file devhost-2.1.1-py3-none-any.whl.
File metadata
- Download URL: devhost-2.1.1-py3-none-any.whl
- Upload date:
- Size: 31.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b579da0621244f80e43ad5b089fa16bf84ad477fe757412ea318d6b23e1b9cb1
|
|
| MD5 |
4f7c6cb82b254a3bcaf566650670f4d3
|
|
| BLAKE2b-256 |
d7ca47d9923693f8e614ee405099752682c3c07141b971d6e882df1403367e44
|
Provenance
The following attestation bundles were made for devhost-2.1.1-py3-none-any.whl:
Publisher:
publish.yml on Patoruzuy/Devhost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
devhost-2.1.1-py3-none-any.whl -
Subject digest:
b579da0621244f80e43ad5b089fa16bf84ad477fe757412ea318d6b23e1b9cb1 - Sigstore transparency entry: 894694109
- Sigstore integration time:
-
Permalink:
Patoruzuy/Devhost@8e84871da292c826b0c5f844044095c3c972fa9d -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/Patoruzuy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8e84871da292c826b0c5f844044095c3c972fa9d -
Trigger Event:
push
-
Statement type: