Remember exe.dev? That magical place where you spin up VMs with SSH and it just works?
This is that. But you host it. On your own VPS. For free. With Docker. Because why pay for something you can self-host?
It's like having your own personal app store, but for your servers. Deploy anything, anywhere, whenever you want.
🤔 "I need to deploy this API quickly"
💭 "Hmm, should I use Kubernetes? Terraform? ArgoCD?"
🧠 [brain explodes]
🎉 "Nah, just use poor man's exe.dev"
| Feature | Description |
|---|---|
| 🚀 Blazing Fast | Deploy apps in seconds, not hours |
| 🔒 Auto-HTTPS | Caddy handles TLS like magic ✨ |
| 🔄 Auto-Updates | Watchtower keeps everything fresh |
| 📦 Resource Guardrails | No container can eat all your RAM |
| 🛡️ Security Hardened | Firewall + Fail2ban pre-configured |
| 📝 Batteries Included | Templates for Node, Python, Static, Fullstack |
| 🤖 CI/CD Ready | GitHub Actions workflows that actually work |
| 🎮 Demo App | A cool AI chat to show off to your friends |
| 🔑 SSH Gateway CLI | exe.dev-style CLI to manage apps via SSH |
# SSH into your server
ssh root@yourserver.com
# Run one command
./deploy.sh demo-ai-chat up
# Boom 💥
→ App is live at https://chat.yourdomain.comThat's it. No kubectl. No helm. No crying.
# SSH into your fresh Ubuntu 22.04/24.04 VPS as root
ssh root@YOUR_SERVER_IP
# Download and run the magic script
curl -LO https://raw.githubusercontent.com/rnzor/poor_man_exe/main/bootstrap/setup.sh
chmod +x setup.sh
./setup.shWhat this does:
- 🐳 Installs Docker + Docker Compose
- 🌐 Installs Caddy (auto-HTTPS wizard)
- 🔥 Sets up UFW firewall
- 🚫 Installs Fail2ban (SSH bodyguard)
- 📡 Installs Watchtower (auto-updates)
- 📁 Creates
/apps/folder structure
cd /apps
git clone https://github.com/rnzor/poor_man_exe.git
cd poor_man_exe/apps/demo-ai-chat
docker compose up -dVisit http://localhost:8080 and say hello to your new AI chat buddy! 🤖
cat >> /etc/caddy/Caddyfile << 'EOF'
chat.yourdomain.com {
reverse_proxy 127.0.0.1:8080
}
EOF
caddy reloadCaddy will:
- Detect the domain
- Provision an SSL certificate (free!)
- Redirect HTTP → HTTPS automatically
- Handle all the TLS nonsense
Chef's kiss 👨🍳✨
A real-time, streaming AI chat application that screams "I know what I'm doing."
- ⚡ WebSocket Streaming - Character-by-character responses like ChatGPT
- 🎨 Beautiful Dark Theme - Cyberpunk vibes, smooth animations
- 📱 Fully Responsive - Works on your phone, laptop, and toaster
- 🐳 Docker Native - Backend + Frontend + Nginx, all containerized
- 💪 Production Ready - Health checks, resource limits, restart policies
┌─────────────────────────────────────────────┐
│ Caddy │
│ (Auto-HTTPS Reverse Proxy) │
└─────────────────────────────────────────────┘
│
┌──────────┴──────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Frontend │ │ Backend │
│ :8080 │ │ :8000 │
│ (React + │ │ (FastAPI + │
│ Nginx) │ │ WebSocket) │
└──────────────┘ └──────────────┘
cd /apps/demo-ai-chat
# Start it up
docker compose up -d
# Check if it's healthy
curl http://localhost:8000/health
# → {"status":"healthy"}
# Watch the magic
docker compose logs -fpoor_man_exe/
├── 📄 README.md # You are here
├── 📄 AGENTS.md # For AI assistants 🤖
│
├── 📂 bootstrap/
│ └── setup.sh # ⭐ The magic bootstrap script
│
├── 📂 templates/
│ ├── Caddyfile # Caddy configuration templates
│ ├── nginx.conf # Nginx config for static sites
│ └── docker-compose/
│ ├── nodejs.yml # Node.js/Express API template
│ ├── python.yml # Python/FastAPI template
│ ├── static.yml # Static site template
│ └── fullstack.yml # Backend + Frontend template
│
├── 📂 apps/
│ └── demo-ai-chat/ # ⭐ The cool demo app
│ ├── backend/ # FastAPI + WebSocket
│ │ ├── Dockerfile
│ │ ├── main.py # The brains
│ │ └── requirements.txt
│ ├── frontend/ # React + TypeScript
│ │ ├── Dockerfile
│ │ ├── src/
│ │ │ ├── App.tsx # The beauty
│ │ │ └── App.css # The style
│ │ └── vite.config.ts
│ ├── docker-compose.yml # Orchestrates everything
│ └── Caddyfile # Domain config
│
├── 📂 scripts/
│ ├── deploy.sh # ⭐ Deployment Swiss Army knife
│ └── logs.sh # Quick log viewer
│
├── 📂 .github/workflows/
│ ├── build-push.yml # Build & push to GHCR
│ └── deploy.yml # Auto-deploy on release
│
└── 📂 docs/
├── QUICKSTART.md # Get running fast
├── DEPLOYMENT.md # How to add apps
├── DOMAINS.md # Domain + HTTPS config
└── TROUBLESHOOTING.md # When things go sideways
cd /apps
mkdir my-awesome-app
cd my-awesome-app
# Pick your poison:
cp ../poor_man_exe/templates/docker-compose/nodejs.yml docker-compose.yml
# or
cp ../poor_man_exe/templates/docker-compose/python.yml docker-compose.yml
# or
cp ../poor_man_exe/templates/docker-compose/static.yml docker-compose.ymlEdit docker-compose.yml:
services:
app:
image: ghcr.io/rnzor/my-awesome-app:latest # <- your image
ports:
- "3000:3000" # <- your port
environment:
- NODE_ENV=productiondocker compose up -dcat >> /etc/caddy/Caddyfile << 'EOF'
myapp.yourdomain.com {
reverse_proxy 127.0.0.1:3000
}
EOF
caddy reloadDone! 🎉 Your app is now live with HTTPS.
The SSH gateway provides an exe.dev-style CLI experience. Connect via SSH and manage your apps directly:
# Connect to the gateway
ssh -p 2222 poor-exe@yourserver.com
# Or run commands directly
ssh -p 2222 poor-exe@yourserver.com ls| Command | Description |
|---|---|
ls |
List your apps with Docker status |
new --name=X [--image=Y] |
Create a new app container |
rm <app> |
Delete an app and its container |
share <cmd> <vm> |
Manage sharing (public/private/port) |
keys [add|rm] |
Manage SSH keys |
whoami |
Show current user info |
help |
Show available commands |
exit |
Disconnect |
# Create a new app with custom image
ssh -p 2222 poor-exe@server.com new --name=myapi --image=node:20-alpine
# List all your apps
ssh -p 2222 poor-exe@server.com ls
# Get JSON output (for scripting)
ssh -p 2222 poor-exe@server.com ls --json
# Attach to an app's shell
ssh -p 2222 myapi@server.com
# Set an app to public
ssh -p 2222 poor-exe@server.com share set-public myapi
# Change the HTTP port
ssh -p 2222 poor-exe@server.com share port myapi 3000
# Add a new SSH key
ssh -p 2222 poor-exe@server.com keys add "ssh-ed25519 AAAA... user@host"
# Remove an app
ssh -p 2222 poor-exe@server.com rm myapiAll commands support --json flag for automation:
ssh -p 2222 poor-exe@server.com ls --json{
"success": true,
"data": {
"vms": [
{"vm_name": "myapi", "image": "node:20-alpine", "status": "running", "created_at": "2026-01-17"}
]
}
}Our Swiss Army knife for deployments:
./deploy.sh <app-name> <command> [options]| Command | Description |
|---|---|
up |
Start the app (default) |
down |
Stop the app |
restart |
Restart the app |
logs -f |
View logs (follow mode) |
status |
Check app status |
update |
Pull latest + rebuild |
pull |
Pull images only |
build |
Build images |
ps |
List containers |
stop |
Stop containers |
start |
Start containers |
clean |
Remove everything |
# Deploy
./deploy.sh demo-ai-chat up
# Watch logs
./deploy.sh demo-ai-chat logs -f
# Update to latest
./deploy.sh demo-ai-chat update
# Check if it's alive
./deploy.sh demo-ai-chat status --port 8080
# Restart everything
./deploy.sh demo-ai-chat restart
# Nuclear option
./deploy.sh demo-ai-chat cleanWatchtower is pre-installed and runs in the background. It:
- ⏰ Polls every 5 minutes
- 📦 Checks for new images
- 🔄 Restarts containers automatically
- 🧹 Cleans up old images
Manual trigger:
docker restart watchtowerTrigger: Push to main
What it does:
- Builds both backend and frontend images
- Pushes to GitHub Container Registry (GHCR)
- Tags with
latestand semantic versions
Trigger: Release published OR manual trigger
What it does:
- SSHs into your server
- Pulls latest code/images
- Rebuilds and restarts containers
- Verifies deployment
Add these in GitHub Settings → Secrets:
| Secret | Value |
|---|---|
SERVER_HOST |
Your server IP |
SERVER_USER |
root (or your SSH user) |
SERVER_SSH_KEY |
Your private SSH key |
All templates include sane defaults to prevent container chaos:
deploy:
resources:
limits:
cpus: '0.5' # Half a CPU
memory: 512M # 512MB RAM
reservations:
cpus: '0.1'
memory: 128MAdjust based on your app's needs. Some apps are hungry. We don't judge.
| Feature | What It Does |
|---|---|
| UFW Firewall | Blocks everything except 22, 80, 443 |
| Fail2ban | Bans bots that brute-force your SSH |
| Non-root Containers | No container runs as root |
| Auto-HTTPS | TLS 1.3 with modern cipher suites |
| Resource Limits | Containers can't eat all your RAM |
| Ed25519 Host Keys | Modern, high-security SSH host keys |
| SHA256 Fingerprints | Proper SSH key fingerprint calculation |
| Rate Limiting | Token bucket rate limiting per IP |
| Session Limits | Max 10 concurrent sessions per key |
| Audit Logging | All operations logged with IP + timestamp |
| Feature | exe.dev | Poor Man's exe.dev |
|---|---|---|
| 💸 Free | ❌ | ✅ |
| 🏠 Self-hosted | ❌ | ✅ |
| 🔒 Auto-HTTPS | ✅ | ✅ |
| 🐳 Docker | ✅ | ✅ |
| 🔌 WebSockets | ✅ | ✅ |
| 📊 Resource limits | ✅ | ✅ |
| 🚀 Fast deploy | ✅ | ✅ |
| 🤖 Custom infrastructure | ❌ | ✅ |
We're basically the open-source, free, self-hosted version. You're welcome.
- 📖 Quick Start - Get running in 5 minutes
- 🚀 Deployment Guide - Add new applications
- 🌐 Domain Config - HTTPS + custom domains
- 🐛 Troubleshooting - Fix stuff
# Deployment script
./deploy.sh demo-ai-chat up
./deploy.sh demo-ai-chat logs -f
./deploy.sh demo-ai-chat update
# Docker directly
docker compose up -d # Start
docker compose down # Stop
docker compose logs -f # Logs
docker compose pull # Update images
docker compose up -d --build # Rebuild
docker stats # Check resources
# Caddy
caddy reload # Reload config
caddy validate # Check config
journalctl -u caddy -f # Caddy logs- 🍴 Fork the repo
- 🌿 Create a feature branch
- ✨ Make it awesome
- 📝 Commit with a fun message
- 📤 Push to your fork
- 🔀 Open a PR
All contributions welcome! Especially ones that make this even more "exe-like."
MIT License - Fork it, break it, fix it, deploy it.
- 📖 Docs
- 🐛 Issues
- 💬 Discussions
Made with ❤️, ☕, and too much Docker
⭐ Star us on GitHub!