How to Expose Local Services with ASD Tunnels
ASD gives you multiple ways to expose local services to the internet. From a quick one-liner to a full project configuration, pick the method that matches your workflow. This guide covers all three approaches and when to use each.
Three ways to create tunnels
| Need | Method | Complexity |
|---|---|---|
| Share something right now | asd expose | Simplest |
| Daily development workflow | asd.yaml config | Medium |
| CI/CD automation | Tunnel tokens | Advanced |
Method 1: Quick expose
The fastest way to share a local port. One command, instant result:
asd expose 3000
Output:
Local: http://localhost:3000
Caddy: http://app.localhost
Tunnel: https://app-abc123.cicd.eu1.asd.engineer
Custom names
Give your tunnel a recognizable URL prefix:
asd expose 3000 --name myapp
# Result: https://myapp-abc123.cicd.eu1.asd.engineer
Direct tunnel (skip Caddy)
If you don't need the local Caddy proxy, use --direct to create
a tunnel straight to your service:
asd expose 3000 myapp --direct
Managing exposed services
# List all exposed services with their URLs
asd expose list
# Stop by name
asd expose stop myapp
# Stop by port
asd expose stop 3000
Best for: Quick demos, one-off sharing, testing webhooks.
Method 2: Project config (asd.yaml)
For ongoing development, define your services in asd.yaml.
This gives you repeatable configuration, multiple services, and local routing
through Caddy.
Initialize your project
cd your-project
asd init
This creates asd.yaml and sets up the workspace.
Define your services
version: 1
project:
name: "my-app"
network:
services:
frontend:
dial: "127.0.0.1:3000"
host: "app.localhost"
public: true
subdomain: "frontend"
api:
dial: "127.0.0.1:8080"
paths: ["/api"]
public: true
subdomain: "api"
Each service gets:
dial— the local address and porthost— an optional local hostname for Caddy routingpaths— path-based routing (e.g.,/api)public: true— enables the public tunnelsubdomain— prefix for the tunnel URL
Apply and start
# Apply config, start Caddy and tunnels
asd net apply --caddy --tunnel
# Open the network TUI to see everything
asd net
The TUI shows all services, their health status, and access URLs:
| Key | Action |
|---|---|
Tab | Cycle tabs (Services, Projects, Logs) |
Enter | Actions menu for selected service |
Ctrl+R | Refresh health status |
Ctrl+Q | Quit |
Best for: Daily development, team projects, multiple services.
Method 3: Tunnel tokens (CI/CD)
For automated environments like GitHub Actions or GitLab CI, use tunnel tokens instead of interactive authentication.
Ephemeral tokens (quick testing)
No account needed. Get 5-minute credentials instantly:
curl -X POST https://asd.engineer/functions/v1/create-ephemeral-token
Persistent tokens (CI/CD pipelines)
- Sign up at asd.host
- Go to Account → Tunnel Tokens → Create
- Add credentials as CI secrets:
ASD_TUNNEL_TOKEN=your-token-from-dashboard
ASD_TUNNEL_USER=your-user-id
Then in your CI pipeline:
asd expose 3000
Best for: CI/CD pipelines, automated testing, webhook receivers.
Multiple services at once
With asd.yaml, you can expose multiple services simultaneously.
Each gets its own tunnel URL:
network:
services:
frontend:
dial: "127.0.0.1:3000"
host: "app.localhost"
public: true
subdomain: "frontend"
api:
dial: "127.0.0.1:8080"
paths: ["/api"]
public: true
subdomain: "api"
docs:
dial: "127.0.0.1:4000"
host: "docs.localhost"
public: true
subdomain: "docs"
asd net apply --caddy --tunnel
All three services get unique tunnel URLs and local Caddy routes.
Access patterns
Every exposed service is accessible three ways:
| Pattern | URL Example | Use Case |
|---|---|---|
| Local Direct | http://localhost:3000 |
Same machine |
| Caddy Route | http://app.localhost |
Local network, LAN |
| Tunnel Remote | https://frontend-abc123.cicd.eu1.asd.engineer |
Anywhere on the internet |
Browser → ASD Cloud → SSH Tunnel → Caddy (local) → Your Service
(encrypted) ↑
Routes + Auth
Adding authentication
By default, anyone with the tunnel URL can access your service. Add a password
with basic_auth in asd.yaml:
network:
caddy:
basic_auth:
enabled: true
realm: "My Project"
services:
my-app:
dial: "127.0.0.1:3000"
public: true
Set credentials in .env:
ASD_BASIC_AUTH_USERNAME=admin
ASD_BASIC_AUTH_PASSWORD=your-secure-password
See the Caddy Basic Auth guide for detailed configuration options.
Troubleshooting
| Problem | Solution |
|---|---|
| Tunnel not connecting | Check credentials: asd tunnel auth status |
| Port already in use | asd expose stop 3000 or lsof -i :3000 |
| Service not in TUI | asd net refresh to re-scan services |
| Caddy won't start | asd caddy stop, remove .asd/workspace/caddy/, restart |
What's next
- Configure asd.yaml — full reference for project configuration
- Add Basic Authentication — protect your exposed services
- Live Terminal in CI — use ASD tunnels in GitHub Actions
Kelvin Wuite
Kelvin Wuite is the founder of Accelerated Software Development B.V. With over eighteen years of development experience, he has witnessed the same patterns repeat across every software team: endless documentation, manual preparation, environment mismatches, and fragmented collaboration. His drive is to remove these barriers, enabling engineers to work together in unified environments with shorter feedback loops and hands-on collaboration. Since 2015 he has been refining these ideas, leading to ASD — a platform designed to create a faster, more integrated way for development teams to collaborate in an age where AI is thriving.
Related Articles
How to Create Your First HTTPS Tunnel in 30 Seconds
Install ASD CLI and expose your local development server to the internet with a public HTTPS URL. One command, 30 seconds, no configuration needed.
ConfigurationHow to Configure asd.yaml for Your Project
Complete guide to configuring asd.yaml: define services, set up networking, add authentication, create automation tasks, and configure plugins for your project.
ServicesHow to Set Up a Web Terminal with ttyd
Set up a browser-based terminal with ttyd and ASD. Access your development machine from any device, anywhere, with password protection and HTTPS tunneling.