automation · Deploy guide
Self-host Activepieces
AI-first, no-code workflow automation. Here's how to run Activepieces on your own infrastructure — no marketing puffery, honest pitfalls, one recommended stack.
Every guide on SwapToSaaS runs on servers we actually pay for. Activepieces's footprint fits comfortably on this class of instance. Different provider preference? The steps below work anywhere Docker runs.
Provision a server →Before you start
Activepieces is a MIT-licensed workflow-automation tool — cleaner UX than n8n, closer to Zapier's step-by-step flow, right-sized for non-developer users. This guide gets you from an empty Hetzner CX22 to a working, TLS-protected Activepieces instance in about 30 minutes.
You'll need:
- A domain (e.g.
ap.yourcompany.com) with DNS pointing to your server. - Basic terminal skills.
- An email address for Let's Encrypt notices.
Step 1 · Provision the server (5 min)
Create a Hetzner CX22 in your preferred datacenter. Ubuntu 24.04 LTS. SSH key. Once it boots, note the IPv4.
ssh root@YOUR_SERVER_IP
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh Step 2 · Point DNS (5 min)
ap.yourcompany.com → YOUR_SERVER_IP Disable Cloudflare orange-cloud proxy until Let's Encrypt succeeds; re-enable after.
Step 3 · Install Docker (5 min)
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker Step 4 · Write the docker-compose stack (10 min)
mkdir -p ~/activepieces && cd ~/activepieces
mkdir -p caddy_data caddy_config postgres_data redis_data ~/activepieces/docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: activepieces
POSTGRES_USER: activepieces
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- ./redis_data:/data
activepieces:
image: activepieces/activepieces:latest
restart: unless-stopped
depends_on:
- postgres
- redis
environment:
AP_FRONTEND_URL: https://${AP_HOST}
AP_ENCRYPTION_KEY: ${AP_ENCRYPTION_KEY}
AP_JWT_SECRET: ${AP_JWT_SECRET}
AP_POSTGRES_DATABASE: activepieces
AP_POSTGRES_HOST: postgres
AP_POSTGRES_PORT: 5432
AP_POSTGRES_USERNAME: activepieces
AP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
AP_REDIS_HOST: redis
AP_REDIS_PORT: 6379
AP_EXECUTION_MODE: UNSANDBOXED
AP_TELEMETRY_ENABLED: 'false'
AP_ENGINE_EXECUTABLE_PATH: dist/packages/engine/main.js
expose:
- "80"
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy_data:/data
- ./caddy_config:/config
depends_on:
- activepieces
~/activepieces/Caddyfile:
${AP_HOST} {
reverse_proxy activepieces:80
} ~/activepieces/.env:
AP_HOST=ap.yourcompany.com
POSTGRES_PASSWORD=RANDOM_32_CHARS
AP_ENCRYPTION_KEY=RANDOM_HEX_32_CHARS
AP_JWT_SECRET=RANDOM_32_CHARS Generate secrets: openssl rand -hex 16 for AP_ENCRYPTION_KEY, openssl rand -base64 24 for the others.
Step 5 · Bring it up (5 min)
cd ~/activepieces
docker compose up -d
docker compose logs -f activepieces First run migrates the database and generates piece definitions. Wait for Server is listening on port 80. Hit Ctrl+C to stop tailing.
Open https://ap.yourcompany.com. Sign up as the first user (that user becomes owner).
Step 6 · Firewall and backups
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable Nightly Postgres dump:
sudo tee /etc/cron.daily/ap-backup > /dev/null <<'EOF'
#!/bin/bash
set -e
BACKUP_DIR=/home/deploy/backups
mkdir -p "$BACKUP_DIR"
docker exec activepieces-postgres-1 pg_dump -U activepieces activepieces | gzip > "$BACKUP_DIR/ap-$(date +%Y%m%d).sql.gz"
find "$BACKUP_DIR" -name "ap-*.sql.gz" -mtime +14 -delete
EOF
sudo chmod +x /etc/cron.daily/ap-backup Common pitfalls
- Piece install fails on first run. Activepieces pulls piece definitions from npm on startup. If your server can't reach npm registry (firewall, DNS), it fails silently. Test
curl https://registry.npmjs.org/from the server. - Encryption key mismatch. If you rotate AP_ENCRYPTION_KEY, existing flow credentials become undecryptable. Set it once and keep it in your backup.
- Sandboxed mode requires cgroups. The default in this compose is
UNSANDBOXED. If you need code-execution sandbox, follow the official docs for cgroup v2 setup on your host. - Webhook URL must match AP_FRONTEND_URL. Same rule as n8n — the outside world must be able to reach that URL.
Updating
cd ~/activepieces
docker compose pull
docker compose up -d
docker image prune -f What you're paying vs Zapier
- Activepieces on Hetzner CX22: €45/year (~$50) + your time.
- Zapier Team (20k tasks/mo): $800+/year.
- Activepieces Cloud (official): free tier + paid plans from $50/mo.
Activepieces self-hosted has one of the best cost-to-power ratios in the whole open-source workflow space. If your team is non-technical, the polished UX also lowers training time versus n8n.
Hosts that work well for Activepieces
Some links are affiliate. We only include hosts we deploy production workloads on. Rankings above are independent of these links.