How to Self-Host Activepieces: Open Source Zapier Alternative 2026
·OSSAlt Team
activepiecesautomationzapierself-hostingdockerworkflows2026
TL;DR
Activepieces (MIT, ~10K GitHub stars, TypeScript) is a fully open source Zapier alternative — no usage limits, no locked features, 100% MIT licensed. Zapier charges $29.99/month for 750 tasks. Activepieces self-hosted gives you unlimited flows, unlimited runs, and all 100+ integrations free. The visual flow builder is clean, each step is a "piece" (Activepieces terminology for integration), and you can write custom TypeScript pieces to add any integration.
Key Takeaways
- Activepieces: MIT, ~10K stars, TypeScript — 100% open source, no feature gating
- 100+ pieces: Slack, Gmail, GitHub, Notion, PostgreSQL, HTTP requests, and more
- Unlimited flows: No task limits on self-hosted — limited only by your hardware
- Custom pieces: Write TypeScript to add any integration not in the catalog
- Simple Docker: Single
docker compose up -dto get started - vs n8n: Activepieces is simpler/cleaner UI; n8n has more integrations and code flexibility
Activepieces vs n8n vs Zapier
| Feature | Activepieces | n8n | Zapier |
|---|---|---|---|
| License | MIT | Sustainable Use | Proprietary |
| GitHub Stars | ~10K | ~51K | — |
| Cost (self-hosted) | Free | Free | $299/mo |
| Integrations | 100+ | 400+ | 6000+ |
| Code nodes | TypeScript | JS + Python | Limited |
| Visual builder | Excellent | Good | Excellent |
| AI/LLM nodes | Yes (basic) | Yes (LangChain) | Limited |
| Custom integrations | TypeScript pieces | Custom nodes | No |
| Learning curve | Low | Medium | Low |
| Best for | Simple automations | Complex workflows | Hosted simplicity |
Part 1: Docker Setup
# docker-compose.yml
services:
activepieces:
image: activepieces/activepieces:latest
container_name: activepieces
restart: unless-stopped
ports:
- "8080:80"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
environment:
AP_ENGINE_EXECUTABLE_PATH: dist/packages/engine/main.js
AP_ENVIRONMENT: prod
AP_FRONTEND_URL: "https://flows.yourdomain.com"
AP_WEBHOOK_TIMEOUT_SECONDS: 30
AP_MAX_FILE_SIZE_MB: 10
AP_TELEMETRY_ENABLED: "false"
AP_SIGN_UP_ENABLED: "true" # Disable after first user setup
# Database:
AP_POSTGRES_DATABASE: activepieces
AP_POSTGRES_HOST: postgres
AP_POSTGRES_PORT: 5432
AP_POSTGRES_USERNAME: activepieces
AP_POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
AP_POSTGRES_USE_SSL: "false"
# Redis:
AP_REDIS_HOST: redis
AP_REDIS_PORT: 6379
# Encryption:
AP_ENCRYPTION_KEY: "${ENCRYPTION_KEY}"
AP_JWT_SECRET: "${JWT_SECRET}"
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: activepieces
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_DB: activepieces
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U activepieces"]
interval: 10s
start_period: 20s
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
# .env
POSTGRES_PASSWORD=your-secure-db-password
ENCRYPTION_KEY=$(openssl rand -hex 16) # Must be exactly 32 chars hex
JWT_SECRET=$(openssl rand -hex 32)
docker compose up -d
Visit https://flows.yourdomain.com → create admin account.
Part 2: HTTPS with Caddy
flows.yourdomain.com {
reverse_proxy localhost:8080
}
Part 3: Building Flows
A flow in Activepieces is:
- One trigger (webhook, schedule, or app event)
- One or more steps (actions from pieces)
Create your first flow
- + New Flow
- Click Trigger → choose trigger type:
- Webhook: Gets a unique URL for external services to call
- Schedule: Cron expression (every hour, daily, etc.)
- App trigger: Event from a specific app (new GitHub issue, Slack message, etc.)
- Click + to add steps
- Each step is a piece — choose app + action
- Reference data from previous steps using
{{step_1.value}}
Part 4: Example Flows
GitHub stars → Slack notification
Trigger: Webhook (configure on GitHub as push event webhook)
OR
Trigger: GitHub — New Star on Repository
→ Repo: your-org/your-repo
Step 1: Slack — Send Message
Channel: #metrics
Message: "🌟 New star! {{ trigger.stargazer.login }} starred {{ trigger.repository.full_name }}
Total stars: {{ trigger.repository.stargazers_count }}"
Form → Google Sheets + email
Trigger: Webhook (POST from your contact form)
Step 1: Data transformation (using Code piece)
Code: return {
name: trigger.body.name,
email: trigger.body.email,
timestamp: new Date().toISOString()
};
Step 2: Google Sheets — Insert Row
Spreadsheet: "Contact Form Submissions"
Sheet: "Leads"
Values: {{ step1.name }}, {{ step1.email }}, {{ step1.timestamp }}
Step 3: Gmail — Send Email
To: sales@yourcompany.com
Subject: "New contact form submission from {{ step1.name }}"
Body: "Name: {{ step1.name }}\nEmail: {{ step1.email }}"
Scheduled daily report
Trigger: Schedule — 0 9 * * 1-5 (weekdays at 9 AM)
Step 1: HTTP Request — GET https://yourapi.com/metrics
Headers: Authorization: Bearer {{ connections.myapi.token }}
Step 2: Code — Format metrics
Code: const data = step1.body;
return {
summary: `Users: ${data.users}, Revenue: $${data.revenue}`,
html: `<b>Users:</b> ${data.users}<br><b>Revenue:</b> $${data.revenue}`
};
Step 3: Slack — Send Message
Channel: #daily-metrics
Message: "📊 Daily Report\n{{ step2.summary }}"
Part 5: Webhook Triggers
# Your flow's webhook URL (from the trigger config panel):
https://flows.yourdomain.com/api/v1/webhooks/FLOW_ID
# Test a webhook:
curl -X POST https://flows.yourdomain.com/api/v1/webhooks/FLOW_ID \
-H "Content-Type: application/json" \
-d '{"event": "payment.completed", "amount": 99.99, "customer": "alice@example.com"}'
# Use as Stripe webhook:
# Stripe Dashboard → Developers → Webhooks → Add endpoint
# URL: https://flows.yourdomain.com/api/v1/webhooks/FLOW_ID
# Events: payment_intent.succeeded
# Use as GitHub webhook:
# Repo Settings → Webhooks → Add webhook
# Payload URL: https://flows.yourdomain.com/api/v1/webhooks/FLOW_ID
# Content type: application/json
Part 6: Custom TypeScript Pieces
If you need an integration that doesn't exist:
// packages/pieces/custom/my-service/src/lib/my-service.piece.ts
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
import { myServiceSendMessage } from './actions/send-message';
import { myServiceNewEvent } from './triggers/new-event';
export const MyServiceAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: 'Your service API key',
required: true,
});
export const myService = createPiece({
displayName: 'My Service',
auth: MyServiceAuth,
minimumSupportedRelease: '0.20.0',
logoUrl: 'https://yourservice.com/logo.png',
authors: ['yourname'],
actions: [myServiceSendMessage],
triggers: [myServiceNewEvent],
});
// packages/pieces/custom/my-service/src/lib/actions/send-message.ts
import { createAction, Property } from '@activepieces/pieces-framework';
import { MyServiceAuth } from '../my-service.piece';
export const myServiceSendMessage = createAction({
name: 'send_message',
auth: MyServiceAuth,
displayName: 'Send Message',
description: 'Send a message via My Service',
props: {
recipient: Property.ShortText({ displayName: 'Recipient', required: true }),
message: Property.LongText({ displayName: 'Message', required: true }),
},
async run(context) {
const { recipient, message } = context.propsValue;
const apiKey = context.auth;
const response = await fetch('https://api.yourservice.com/messages', {
method: 'POST',
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ to: recipient, body: message }),
});
return response.json();
},
});
Part 7: Connections (Credentials)
Manage credentials centrally in Connections:
- Connections → + New Connection
- Select a piece type (GitHub, Slack, Google, etc.)
- OAuth2 pieces: click Connect → authenticate
- API key pieces: enter your key
- All flows in your workspace can reuse connections
Maintenance
# Update Activepieces:
docker compose pull
docker compose up -d
# Backup database:
docker exec activepieces-postgres-1 pg_dump -U activepieces activepieces \
| gzip > activepieces-backup-$(date +%Y%m%d).sql.gz
# Logs:
docker compose logs -f activepieces
# Disable new signups after admin setup:
# Set AP_SIGN_UP_ENABLED=false in docker-compose.yml then restart
See all open source automation tools at OSSAlt.com/categories/automation.