Getting Started with EPD
Deploy any application to one server or a fleet of servers with zero downtime, automatic Let's Encrypt SSL, and a shared Traefik proxy.
This guide will take you from zero to a live production deployment in under 5 minutes.
Prerequisites
Before starting, ensure you have:
A Linux Server (VPS):
- Any clean Linux VPS (Ubuntu 22.04/24.04, Debian 11/12, Fedora, Arch, etc.)
- An open SSH port (22) with root or passwordless
sudoaccess (or password to allow EPD to configure it). - Public ports 80 (HTTP) and 443 (HTTPS) accessible from the internet.
- A DNS
Arecord pointing your domain to the server's public IP address.
Local Machine:
- Linux or macOS (or Windows via WSL2).
- Standard
sshclient. - Docker installed locally (if deploying in Docker mode to build images).
No Server Yet?
If you don't have a server, you can provision one in 30 seconds using the built-in Hetzner Cloud integration:
epd hetzner create my-server --setup1. Install EPD
Install epd using the zero-dependency standalone installer (requires no Node.js or Bun runtime), or via your favorite package manager:
curl -fsSL https://raw.githubusercontent.com/FabioPlunser/epd/main/install.sh | bashbun install -g @fabioplunser/epdnpm install -g @fabioplunser/epdpnpm add -g @fabioplunser/epdnpx @fabioplunser/epd --helpVerify installation:
epd --version2. Shell Autocompletion (Optional)
Enable instant tab-completion for all commands, flags, and destination overlays:
eval "$(epd completion zsh)"source <(epd completion bash)epd completion fish | source3. Initialize Your Project
Navigate to your application's root directory:
cd my-app
epd initepd init will inspect your project:
- Automatically detects your runtime (Node.js, Bun, Python, Go, Ruby, or static).
- Detects whether you already have a
Dockerfile. - Prompts for your server IP, domain name, and ACME SSL email.
- Generates a tailored
epd.ymlconfig file (and an optimized starterDockerfileif needed).
GitHub Actions Starter
Run epd init --ci or epd gh-action to also generate a ready-to-run GitHub Actions workflow (.github/workflows/deploy.yml).
4. Configure epd.yml
Open epd.yml in your editor. Here is a typical single-server configuration:
# Application name (used for container naming and routing isolation)
name: my-app
mode: docker
# Docker image name (built locally, or pulled from registry)
image: my-app
# SSH connection details
ssh:
user: root
# Server & Routing
servers:
web:
hosts: [203.0.113.10]
port: 3000
domains: [app.example.com]
# Shared Traefik Proxy & Automatic SSL
proxy:
email: admin@example.com
# Health check probe before switching live traffic
healthcheck:
path: /health
timeout: 30
# Environment variables & secrets
env:
clear:
NODE_ENV: production
secret:
- DATABASE_URL# Run code directly under PM2 without Docker on the host
name: my-app
mode: process
process:
install: bun install --frozen-lockfile
build: bun run build
start: bun run start
exclude: [".git", "node_modules", ".env", "dist"]
ssh:
user: root
servers:
web:
hosts: [203.0.113.10]
replicas: 2
port: 3000
domains: [app.example.com]
proxy:
email: admin@example.comSecrets Setup
Any secret declared under env.secret is resolved automatically from your local shell or .env file:
# In your local .env or current shell:
export DATABASE_URL="postgres://user:password@host/dbname"When you deploy, epd securely uploads secrets into a root-owned 0600 file on the remote server without saving secrets in git or container image layers.
5. Pre-flight Checks (epd doctor)
Run epd doctor to verify your local tools, secrets, and server reachability before deploying:
epd doctorepd doctor confirms:
- Local Bun/Node, Docker engine, and clean git state
- SSH client and key permissions (
0600) epd.ymlsyntax and.envsecret availability- Remote server SSH reachability, passwordless sudo, Docker health, and free disk space
6. Run Server Setup
Run epd setup once for each new server:
epd setupAutomated SSH & Server Onboarding:
- Password Prompt: If your server only has password login enabled,
epd setupprompts for the server password once. - Key Installation: It installs your public key (
~/.ssh/id_ed25519.pub) to~/.ssh/authorized_keys. - Passwordless Sudo: Configures
/etc/sudoers.d/epdautomatically so future commands run non-interactively. - Docker & Traefik: Installs Docker and starts Traefik reverse proxy with Let's Encrypt SSL.
- Initial Deploy: Builds your image, boots slot
blue, verifies health checks, and registers routes.
7. Deploy Updates with Zero Downtime
Whenever you commit changes or want to deploy an update:
epd deployWant to preview the deployment plan without changing anything on the servers? Run a dry run:
epd deploy --dry-runepd deploy executes a zero-downtime blue/green swap:
- Builds the latest container image (or syncs process code).
- Starts the new version in the idle slot (e.g.,
green). - Probes the internal health check endpoint until healthy.
- Atomically updates Traefik's dynamic routing to direct incoming requests to the new slot.
- Drains in-flight connections to the previous slot (
blue) before gracefully stopping it.
Rollback Anytime
If an issue occurs after deployment, revert instantly to the previous version with:
epd rollbackThis re-activates the previous slot in seconds without rebuilding or re-uploading!
8. Inspect & Monitor
Use epd CLI commands to check your application's health and operations:
# Check running containers, active slots, and route status
epd status
# Stream live container logs
epd logs -f
# Real-time multi-server CPU, memory, and container monitor
epd top
# Inspect Let's Encrypt SSL/TLS certificates and days to expiry
epd ssl
# Toggle zero-downtime 503 maintenance page
epd maintenance on -m "Upgrading database. Back in 10 minutes."
epd maintenance off
# Create compressed database / volume backups
epd backup db
# Open an interactive shell inside a running replica
epd shellNext Steps
Now that your application is live, explore additional capabilities:
- Architecture & Concepts: How Traefik routing, blue/green slots, and failover work.
- Deployment Modes: Compare Docker vs. Process (PM2) modes.
- Multi-Site Hosting: Host 5, 10, or 20 distinct domains on a single VPS.
- Accessories & Databases: Spin up PostgreSQL, Redis, or MinIO alongside your app.
- GitHub Actions CI/CD: Set up fully automated deployments on every
git push. - Hetzner Cloud & DNS: Automate server provisioning and Cloudflare/Hetzner DNS sync.
- CLI Reference: Complete list of all CLI commands and flags.