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. - 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:
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 globally using your preferred package manager or download the standalone binary:
bun install -g @fabioplunser/epdnpm install -g @fabioplunser/epdpnpm add -g @fabioplunser/epdnpx @fabioplunser/epd --helpcurl -fsSL https://github.com/FabioPlunser/epd/releases/latest/download/epd-linux-x64 -o /usr/local/bin/epd
chmod +x /usr/local/bin/epdVerify installation:
epd --version2. 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 to also generate a ready-to-run GitHub Actions workflow (.github/workflows/deploy.yml).
3. 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.
4. Run Server Setup
Run epd setup once for each new server:
epd setupWhat epd setup does automatically:
- Validates SSH Connectivity to all configured servers.
- Installs Host Prerequisites: Installs Docker (or PM2 and Node/Bun in process mode) if not already present.
- Provisions Shared Traefik Reverse Proxy: Launches Traefik listening on ports 80 and 443 with Let's Encrypt automated ACME SSL certificate issuance.
- Creates Network & Storage: Configures the shared Docker bridge network (
epd) and volume directories (/var/lib/epd). - Runs Your First Deployment: Builds the app, starts the blue slot, verifies health checks, and registers the domain in Traefik.
5. Deploy Updates with Zero Downtime
Whenever you commit changes or want to deploy an update:
epd deployepd 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!
6. Inspect & Monitor
Use epd CLI commands to check your application's health:
# Check running containers, active slots, and route status
epd status
# Stream live container logs
epd logs -f
# Follow logs specifically from your web service
epd logs --service web -f --lines 100
# 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.