Deploying Activepieces on Hetzner Using Docker Compose

Deploying Activepieces on Hetzner Using Docker Compose

What You’ll Need

  • A cloud instance from Hetzner VPS or Contabo VPS running Ubuntu 22.04 LTS
  • A domain name registered with Namecheap
  • An account with DigitalOcean if you prefer an alternative server provider
  • Basic familiarity with open source automation engines compared against n8n Cloud or SaaS alternatives like Make.com

Table of Contents

Provisioning Your Hetzner Server and Firewall Setup

Self-hosting an automation engine gives you absolute control over your execution environment, data privacy, and monthly compute costs. Activepieces is an open-source, TypeScript-based workflow automation platform designed to be lightweight, modular, and fast. Deploying it on a high-performance cloud server provides a rock-solid infrastructure for processing webhooks, API requests, and scheduled background jobs.

To begin, log into your account on Hetzner VPS and spin up a new Cloud Server instance. For production workloads with multiple active flows, I recommend selecting at least a CX22 or CX32 server instance running Ubuntu 22.04 LTS. Assign a static IPv4 address to your server so your domain records remain stable.

Once your server boots, SSH into the machine as the root user:

ssh root@your_server_ip

First, update your package cache and upgrade all existing system dependencies to their latest stable releases:

apt update && apt upgrade -y

Next, create a dedicated non-root deploy user with sudo privileges to manage your container environment safely:

useradd -m -s /bin/bash deploy
usermod -aG sudo deploy
passwd deploy

Switch to the newly created deploy user and set up Docker’s official repository:

su - deploy
sudo apt-get install -y ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /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-compose-plugin
sudo usermod -aG docker deploy

Log out and back in as deploy to ensure your shell inherits the new Docker group permissions. Test that Docker runs without root privileges by executing:

docker run hello-world

Network security must be configured before exposing any application services to the public internet. You should follow best practices for Configuring UFW Firewall Rules On Linux Servers to restrict unauthorized access to your instance. Run the following commands to block all inbound traffic except OpenSSH, HTTP, and HTTPS:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.

Activepieces Architecture and Docker Compose Configuration

Activepieces relies on three core services to execute automation tasks reliably:

  1. PostgreSQL database for storing user accounts, flow definitions, execution history, and connection credentials.
  2. Redis instance for managing job queues, worker coordination, and pub/sub event distribution.
  3. Activepieces container hosting the engine, web dashboard, and sandboxed code execution workers.

Create a dedicated directory on your server to house your stack configurations:

mkdir -p ~/activepieces
cd ~/activepieces

We need to generate two distinct cryptographically secure secrets for session encryption and database protection. Generate these secrets using OpenSSL:

openssl rand -hex 32
openssl rand -hex 32

Create a .env file inside ~/activepieces using your terminal editor. Populate every configuration variable fully without missing keys:

AP_ENGINE_EXECUTOR=SANDBOXED
AP_CONTAINER_SANDBOX_TIMEOUT_IN_SECONDS=600
AP_ENVIRONMENT=prod
AP_ENCRYPTION_KEY=e4f82a938c1b472e90f1d2c3b4a5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3
AP_JWT_SECRET=9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b
AP_FRONTEND_URL=https://automation.yourdomain.com
AP_POSTGRES_DATABASE=activepieces
AP_POSTGRES_USER=activepieces_user
AP_POSTGRES_PASSWORD=super_secure_postgres_password_9872
AP_POSTGRES_HOST=postgres
AP_POSTGRES_PORT=5432
AP_REDIS_HOST=redis
AP_REDIS_PORT=6379
AP_REDIS_PASSWORD=super_secure_redis_password_6541
AP_TELEMETRY_ENABLED=false

Now create the docker-compose.yml file in the same directory. This file defines the full dependency tree, volume mounts, network boundaries, and environment configurations:

version: '3.8'

networks:
  activepieces-net:
    driver: bridge

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local
  activepieces_data:
    driver: local

services:
  postgres:
    image: postgres:15-alpine
    container_name: activepieces-postgres
    restart: always
    env_file:
      - .env
    environment:
      POSTGRES_DB: ${AP_POSTGRES_DATABASE}
      POSTGRES_USER: ${AP_POSTGRES_USER}
      POSTGRES_PASSWORD: ${AP_POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - activepieces-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${AP_POSTGRES_USER} -d ${AP_POSTGRES_DATABASE}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: activepieces-redis
    restart: always
    command: redis-server --requirepass ${AP_REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    networks:
      - activepieces-net
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${AP_REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  activepieces:
    image: activepieces/activepieces:latest
    container_name: activepieces-app
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file:
      - .env
    environment:
      AP_POSTGRES_HOST: postgres
      AP_POSTGRES_PORT: 5432
      AP_POSTGRES_DATABASE: ${AP_POSTGRES_DATABASE}
      AP_POSTGRES_USER: ${AP_POSTGRES_USER}
      AP_POSTGRES_PASSWORD: ${AP_POSTGRES_PASSWORD}
      AP_REDIS_HOST: redis
      AP_REDIS_PORT: 6379
      AP_REDIS_PASSWORD: ${AP_REDIS_PASSWORD}
      AP_ENCRYPTION_KEY: ${AP_ENCRYPTION_KEY}
      AP_JWT_SECRET: ${AP_JWT_SECRET}
      AP_FRONTEND_URL: ${AP_FRONTEND_URL}
      AP_ENGINE_EXECUTOR: ${AP_ENGINE_EXECUTOR}
      AP_CONTAINER_SANDBOX_TIMEOUT_IN_SECONDS: ${AP_CONTAINER_SANDBOX_TIMEOUT_IN_SECONDS}
      AP_ENVIRONMENT: ${AP_ENVIRONMENT}
      AP_TELEMETRY_ENABLED: ${AP_TELEMETRY_ENABLED}
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - activepieces_data:/usr/src/app/dist
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - activepieces-net

This stack routes the application traffic through port 8080 restricted to localhost (127.0.0.1). This isolates Activepieces from direct public internet exposure until our reverse proxy handles incoming requests.

Configuring Caddy Reverse Proxy and SSL Certificates

To expose Activepieces securely over HTTPS, we use Caddy as our edge reverse proxy. Caddy automatically provisions and renews TLS certificates via Let’s Encrypt without external cron jobs or Certbot configurations.

Head over to Namecheap and update your domain settings. Add an A record pointing automation.yourdomain.com directly to the public IP address of your server.

Install Caddy natively on Ubuntu:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Now overwrite the default Caddyfile located at /etc/caddy/Caddyfile with a clean configuration tailored for Activepieces:

automation.yourdomain.com {
    encode gzip zstd

    reverse_proxy 127.0.0.1:8080 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }

    log {
        output file /var/log/caddy/activepieces_access.log {
            roll_size 10mb
            roll_keep 10
        }
    }
}

Validate your Caddyfile for syntax issues and reload the systemd service:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Start the Docker Compose deployment:

cd ~/activepieces
docker compose up -d

Verify that all three containers are healthy and running:

docker compose ps

You can now open https://automation.yourdomain.com in your web browser. You will be greeted by the Activepieces onboarding screen where you can create your primary administrative owner account.

If you are running multiple microservices on the same server alongside Activepieces, such as a backend database engine detailed in Deploying Self-Hosted PocketBase on Cloud Servers, Caddy will manage separate hostnames effortlessly within that single /etc/caddy/Caddyfile.

For log collection across your application suite, consider Configuring Centralized Log Aggregation with Grafana Loki to stream Caddy logs, Postgres output, and Activepieces runtime logs into a single unified monitoring dashboard.

Day-2 Operations: Backup, Updates, and Monitoring

Deploying the stack is only step one. Managing state, performing automated backups, and upgrading Activepieces reliably ensure your workflow automation engine stays online without loss of data.

Database and Configuration Backups

Create an automated backup script located at /home/deploy/activepieces/backup.sh:

#!/bin/bash
set -e

BACKUP_DIR="/home/deploy/backups/activepieces"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
CONTAINER_NAME="activepieces-postgres"
DB_USER="activepieces_user"
DB_NAME="activepieces"

mkdir -p "${BACKUP_DIR}"

echo "[${TIMESTAMP}] Starting PostgreSQL backup..."
docker exec -t ${CONTAINER_NAME} pg_dump -U ${DB_USER} -d ${DB_NAME} -F c > "${BACKUP_DIR}/db_backup_${TIMESTAMP}.dump"

echo "[${TIMESTAMP}] Backing up environment files..."
cp /home/deploy/activepieces/.env "${BACKUP_DIR}/env_backup_${TIMESTAMP}"

echo "[${TIMESTAMP}] Pruning backups older than 14 days..."
find "${BACKUP_DIR}" -type f -mtime +14 -delete

echo "[${TIMESTAMP}] Backup completed successfully."

Make the script executable:

chmod +x /home/deploy/activepieces/backup.sh

To automate daily execution at 2:00 AM, add a task to your user crontab:

crontab -e

Append the following line to the crontab file:

0 2 * * * /home/deploy/activepieces/backup.sh >> /home/deploy/backups/backup.log 2>&1

Performing Zero-Downtime Container Upgrades

When a new release of Activepieces is published, update your running instance by pulling the latest image tags, recreating the containers, and pruning old unused layers:

cd ~/activepieces
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f

Because database migrations are handled internally by the Activepieces backend on startup, the application automatically upgrades its internal state schemas before opening ports to serve web requests.

Getting Started

Deploying Activepieces on Hetzner VPS or Contabo VPS gives you an exceptionally fast, cost-effective automation stack without execution limits or payload throttling. By provisioning your server securely, tying DNS records through Namecheap, running services via Docker Compose, and terminating SSL with Caddy, you build a production-grade infrastructure tailored for demanding modern integrations.

Outsource Your Automation

Don’t have time? I build production n8n workflows, WhatsApp bots, and fully automated YouTube Shorts pipelines. Hire me on Fiverr, mention SYS3-HUGO for priority. Or DM at chasebot.online.

Want to automate this yourself?

Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.

Want this engine running on your own VPS?

This blog publishes itself — daily, unattended, on free API tiers. The full engine, Hugo theme, and setup guide are available as System 3.

Get System 3
system online