A $6/month VPS can run five side projects, three APIs, a portfolio site, and a staging environment, all at the same time. Most developers don't know this because they think hosting multiple apps requires multiple servers or complicated load balancer configurations.
It doesn't. You need one tool: Traefik.
What Traefik Does
Traefik is a reverse proxy that sits in front of all your services. When an HTTP request arrives, Traefik reads the Host header, matches it against routing rules you've defined, and forwards the request to the correct backend process on the correct port.
One server. Multiple apps. Each with their own domain. Each with their own SSL certificate.
The Architecture
Internet
│
▼
[Your VPS: IP 1.2.3.4]
│
▼
Traefik (ports 80, 443)
├── portfolio.digitalweb.host → localhost:3001
├── api.digitalweb.host → localhost:4000
├── staging.digitalweb.host → localhost:8080
└── client-demo.digitalweb.host → localhost:5000
Each app listens on a different port. Traefik maps each domain to the right port. SSL is handled by Traefik automatically; your apps only need to speak plain HTTP internally.
Prerequisites
- A VPS with a public IP address (any cloud provider works: DigitalOcean, Hetzner, Vultr, Linode)
- Docker and Docker Compose installed
- Port 80 and 443 open in your firewall
- A DomainDock account (free) for custom domain provisioning
Step 1: Set Up the Directory Structure
mkdir -p ~/traefik/dynamic ~/traefik/letsencrypt
touch ~/traefik/letsencrypt/acme.json
chmod 600 ~/traefik/letsencrypt/acme.json
The dynamic directory is where Traefik watches for routing rule files. acme.json is where Let's Encrypt certificates are stored; it must be readable only by root.
Step 2: Write the Traefik Static Configuration
Create ~/traefik/traefik.yml:
api:
dashboard: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: you@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
providers:
file:
directory: /etc/traefik/dynamic
watch: true
Key points:
- Port 80 redirects to 443 automatically
- Let's Encrypt certificates are issued via HTTP-01 challenge
- The
fileprovider watches thedynamicdirectory for routing config files
Step 3: Start Traefik with Docker Compose
Create ~/traefik/docker-compose.yml:
services:
traefik:
image: traefik:v3.3
ports:
- "80:80"
- "443:443"
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
Start it:
cd ~/traefik && docker compose up -d
Traefik is now running and watching the dynamic directory for routing rules.
Step 4: Claim Your Custom Domains on DomainDock
This is where DomainDock plugs in. Instead of manually editing Cloudflare DNS records for each project, DomainDock handles it all from a dashboard.
- Log in to DomainDock
- Create a project for each app (or one project for all of them)
- For each domain, enter your VPS IP address and the port your app runs on
- DomainDock creates the DNS
Arecord on Cloudflare automatically
Within 30–60 seconds, yourapp.digitalweb.host resolves to your VPS.
Step 5: Write Routing Rules for Each App
For each domain, create a YAML file in ~/traefik/dynamic/. Traefik hot-reloads these without restart.
~/traefik/dynamic/portfolio.yml:
http:
routers:
portfolio:
rule: "Host(`portfolio.digitalweb.host`)"
service: portfolio-svc
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
portfolio-svc:
loadBalancer:
servers:
- url: "http://127.0.0.1:3001"
~/traefik/dynamic/api.yml:
http:
routers:
api:
rule: "Host(`api.digitalweb.host`)"
service: api-svc
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
api-svc:
loadBalancer:
servers:
- url: "http://127.0.0.1:4000"
Repeat for each app. Traefik detects the new files and starts routing immediately.
Step 6: Deploy Your Apps on the Configured Ports
Each app just needs to listen on its assigned port. No SSL configuration needed, because Traefik terminates TLS before the request reaches your app.
Node.js example:
const app = express();
// ...your routes...
app.listen(3001, '127.0.0.1', () => {
console.log('Portfolio running on port 3001');
});
Binding to 127.0.0.1 (loopback only) means your app isn't directly exposed to the internet; all traffic comes through Traefik first.
Automating with DomainDock
The manual routing rule approach above works, but it's repetitive. DomainDock's worker service can write Traefik config files automatically when you create or update a domain in the dashboard. This is the same workflow used internally for all domains hosted on the platform.
If you're running DomainDock's worker alongside Traefik, route config files are written to the dynamic directory automatically whenever a domain is provisioned, with no manual YAML editing.
Handling Multiple Apps in Docker
If your apps run in Docker containers, you can use Traefik's Docker provider instead of file-based routing:
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.digitalweb.host`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=3000"
Traefik reads labels from running containers and generates routes on the fly. Add the traefik_network to both the Traefik container and your app containers so they can communicate.
Tips for Running Many Apps on One VPS
Keep apps stateless where possible. If your app writes to local disk, data is lost when the container restarts. Use an external database or object storage.
Use different port ranges per project type. For example: 3000–3099 for frontend apps, 4000–4099 for APIs, 8000–8099 for staging environments. Keeps ports from colliding.
Monitor with DomainDock health checks. Once your domains are created in DomainDock, the platform runs periodic health checks and shows status history. You'll know if a service goes down before your users tell you.
Use Docker restart policies. restart: unless-stopped in your Compose file means apps come back automatically after a server reboot.
Size your VPS correctly. A 1 GB RAM VPS can run 3–4 lightweight Node or Go services comfortably. Add 1 GB RAM for each heavyweight app (Next.js SSR, Python ML inference, etc.).
What This Costs
- VPS: $4–$6/month (Hetzner CX11, DigitalOcean Basic, or Vultr Cloud Compute)
- Domains: Free on DomainDock for
*.digitalweb.host - SSL: Free via Let's Encrypt (Traefik handles renewal automatically)
- Monitoring: Free on DomainDock
Total: $4–6/month for unlimited hosted projects with custom domains and SSL. That's less than one coffee.
Set up your first domain for free at DomainDock. Your apps are already running. They just need a clean address.