Development

Activepieces: Die echte Open-Source-Alternative zu n8n für SaaS-Entwickler

Hendrik Haustein
Hendrik Haustein
Activepieces: Die echte Open-Source-Alternative zu n8n für SaaS-Entwickler
⏱️

In 30 Sekunden

  • Das Problem: n8n ist mächtig, aber die "Fair-Code"-Lizenz und der Ressourcenhunger limitieren SaaS-Projekte.
  • Die Lösung: Activepieces ist MIT-lizenziert, schlanker und setzt konsequent auf TypeScript.
  • Der Clou: Native npm-Pakete in Code-Nodes machen komplexe Workflows zum Kinderspiel.
  • Praxis: Mit einem Browserless-Sidecar bauen wir in 5 Minuten einen vollautomatischen Web-Scraper.
ℹ️

Warum Activepieces?

N8n ist der Platzhirsch im Self-Hosted-Bereich. Keine Frage. Aber wenn du SaaS-Produkte baust, stößt du oft auf Lizenz-Hürden. Activepieces ist die Antwort für alle, die TypeScript lieben und keine Lust auf Boilerplate haben.

1. Warum Activepieces besser für SaaS-Devs ist als n8n

Ich mag n8n. Aber Activepieces gewinnt in drei entscheidenden Punkten, wenn du Software als Service baust:

  1. Die Lizenz (The Big One): Activepieces läuft unter der MIT-Lizenz. Du kannst es forken, verändern, in dein Produkt einbauen und kommerziell nutzen – ohne Rechtsabteilung. n8n nutzt eine eigene Lizenz, die "echte" kommerzielle Konkurrenzprodukte einschränkt.
  2. TypeScript First: Die "Code Node" ist kein Nachgedanke. Du importierst npm-Pakete, und die Engine installiert sie zur Laufzeit in der Sandbox. Es fühlt sich nach "Managed Scripts" an, nicht nach Low-Code-Gefrickel.
  3. Weniger Bloat: Das Interface ist minimalistischer. Für Devs eine Wohltat, da weniger visuelles Rauschen die Arbeit ablenkt.

2. Die Installation: Der "fehlerfreie" Docker-Weg

Hier bin ich ehrlich: Die Standard-Installation von Activepieces per Docker ist oft zickig. Die Dokumentation verschweigt gerne, dass bestimmte Umgebungsvariablen für die Produktion zwingend sind oder dass man ohne spezielle Flags in einem "Invitation Loop" feststeckt.

Damit du dir 2 Stunden Debugging sparst, nutze diese docker-compose.yml. Sie enthält bereits den Browserless-Container für unser Scraper-Tutorial und umgeht die Registrierungssperre.

docker-compose.ymlplaintext
services:
activepieces:
  image: activepieces/activepieces:latest
  container_name: activepieces
  restart: always
  ports:
    - "8080:80"
  environment:
    # --- System & Edition ---
    - AP_ENVIRONMENT=prod           # Wichtig: Prod erzwingt korrekte Flags
    - AP_EDITION=ce                 # Community Editon
    - AP_SIGN_UP_TERMINATED=false   # Erlaubt Registrierung (Fix Invitation-Bug)
    - AP_NODE_EXECUTABLE_PATH=/usr/local/bin/node

    # --- URLs ---
    - AP_FRONTEND_URL=http://localhost:8080
    - AP_WEBHOOK_URL=http://localhost:8080

    # --- Security ---
    - AP_JWT_SECRET=super_secret_jwt_token_fuer_local_dev_12345
    - AP_ENCRYPTION_KEY=00112233445566778899aabbccddeeff

    # --- Database (PostgreSQL) ---
    - AP_POSTGRES_HOST=postgres
    - AP_POSTGRES_PORT=5432
    - AP_POSTGRES_DATABASE=activepieces
    - AP_POSTGRES_USERNAME=activepieces
    - AP_POSTGRES_PASSWORD=password

    # --- Redis & Queue ---
    - AP_REDIS_HOST=redis
    - AP_REDIS_PORT=6379
    - AP_QUEUE_MODE=REDIS

    # --- Execution ---
    - AP_ENGINE_EXECUTOR=SANDBOXED # isoliert Code für die Sicherheit
  depends_on:
    - postgres
    - redis

postgres:
  image: postgres:15-alpine
  container_name: postgres
  environment:
    - POSTGRES_USER=activepieces
    - POSTGRES_PASSWORD=password
    - POSTGRES_DB=activepieces
  volumes:
    - postgres_data:/var/lib/postgresql/data

redis:
  image: redis:7-alpine
  container_name: redis
  volumes:
    - redis_data:/data

# --- browserless optional, für das Tutorial
browserless:
  image: browserless/chrome:latest
  container_name: browserless
  restart: always
  ports:
    - "3000:3000"
  environment:
    - MAX_CONCURRENT_SESSIONS=10

volumes:
postgres_data:
redis_data:

Starte das Ganze mit docker-compose up -d.

⚠️

Wichtig

Falls du vorher mit einer kaputten Config experimentiert hast, führe erst docker-compose down -v aus, um die alten DB-Volumes zu löschen.


3. Tutorial: Einen robusten Blog-Scraper bauen

Jetzt bauen wir etwas Nützliches: Wir scrapen promptbard.de/blog/, analysieren die Kategorien und speichern die Statistiken in ein Google Sheet.

Dank Browserless (einem Headless Chromium) können wir auch Seiten scrapen, die stark auf JavaScript setzen.

Schritt 1: Der Scheduler

Wer mit n8n Erfahrung hat, der bewegt sich auch in Activepieces sofort selbstständig, da bekannt.

Wir fügen einen Scheudler hinzu.Wir fügen einen Scheudler hinzu.

Schritt 2: Die Code Node (Puppeteer)

Vergiss simple HTML-Parser. Wir brauchen puppeteer-core.

  1. Erstelle eine Code Node.
  2. Gehe oben auf den Reiter package.json.
  3. Füge puppeteer-core hinzu. Activepieces holt automatisch die aktuelle Version.

Die Nodes oder Pieces sind ähnlich wie in n8n aufgebaut.Die Nodes oder Pieces sind ähnlich wie in n8n aufgebaut.

Activepieces zieht die Abhängigkeiten automatisch in aktueller Version.Activepieces zieht die Abhängigkeiten automatisch in aktueller Version.

Füge dann folgenden Code in die Node ein:

code-node.tsplaintext
import puppeteer from 'puppeteer-core';

export const code = async (inputs) => {
// Verbindung zum Sidecar-Container
// Im Docker-Netzwerk heißt der Host 'browserless'
const browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://browserless:3000' 
});

try {
  const page = await browser.newPage();
  
  // Wir faken einen echten User-Agent
  await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36');

  await page.goto('https://promptbard.de/blog/', {
    waitUntil: 'networkidle2',
    timeout: 60000
  });

  // Warten, bis die Badges im DOM sind (wichtig für JS-Rendering!)
  await page.waitForSelector('.font-black.tracking-widest', { timeout: 10000 });

  const stats = await page.evaluate(() => {
    // Wir selektieren nach Struktur, nicht nach Farbe (Tailwind-Best-Practice)
    const badges = Array.from(document.querySelectorAll('span.font-black.tracking-widest'));
    const counts: Record<string, number> = {};
    
    badges.forEach(badge => {
      const text = badge.textContent?.trim() || 'Unkategorisiert';
      counts[text] = (counts[text] || 0) + 1;
    });

    return {
      total_categories: Object.keys(counts).length,
      total_articles: badges.length,
      breakdown: counts
    };
  });

  await browser.close();

  // --- Transformation für Google Sheets ---
  // Wir flachen das Objekt hier direkt ab, um Mapping-Chaos zu vermeiden
  const d = new Date();
  const dateStr = `${d.getDate().toString().padStart(2, '0')}.${(d.getMonth() + 1).toString().padStart(2, '0')}.${d.getFullYear()}`;

  return {
    datum: dateStr,
    gesamt: stats.total_articles,
    kategorien_anzahl: stats.total_categories,
    // Mapping der spezifischen Kategorien
    development: stats.breakdown['Development'] || 0,
    ki_ressourcen: stats.breakdown['Künstliche Intelligenz'] || 0,
    saas_tipps: stats.breakdown['SaaS-Marketing'] || 0
  };

} catch (error) {
  if (browser) await browser.close();
  return { error: error.message };
}
};

Ergebnis: Das sammelt der Scraper.Ergebnis: Das sammelt der Scraper.

Schritt 2: Google Sheets Integration

Hier scheitern viele, weil Activepieces keine Spalten für dich erstellt.

  1. Sheet vorbereiten: Erstelle ein neues Google Sheet und schreibe in die erste Zeile die Header: Datum, Gesamt, Kategorien Count, Development, KI-Ressourcen, SaaS-Tipps.

Wir müssen die Sheet vorbereiten.Wir müssen die Sheet vorbereiten.

  1. Node hinzufügen: Wähle Google Sheets -> Insert Row.

So sollte unser Flow mittlerweile aussehen.So sollte unser Flow mittlerweile aussehen.

  1. Verbindung: Logge dich ein und wähle dein Sheet.
  2. Der Trick: Klicke jetzt auf "Refresh Headers" (oder "Load Data"). Erst dann sieht Activepieces deine Spalten.
  3. Mapping: Klicke in die Felder und weise die Werte aus step_1 (unserem Code) zu.

Die Rows zuweisen in Sheets-Node.Die Rows zuweisen in Sheets-Node.

So sollte das Endergebnis aussehen.So sollte das Endergebnis aussehen.

Was können wir damit jetzt machen? Vielleicht:

  • Scrape deine Wettbewerber und beobachte ihre Content-Strategie.
  • Baue den Workflow so weit aus, dass KI Einschätzungen zu den Ergebnissen gibt (oder gleich Entwürfe schreibt).

4. Fazit: Kontrolle statt Komfort-Falle

Activepieces ist (noch) nicht so poliert wie n8n. Manche Integrations fehlen noch. Aber für SaaS-Entwickler, die volle Kontrolle über den Code und die Lizenz wollen, ist es die bessere Wahl.

Du tauschst ein paar Klick-Bunti-Features gegen volle Lizenzfreiheit und echte TypeScript-Power. Mit der obigen Config umgehst du die größten Hürden beim Start.


Häufige Fragen zu Activepieces

Für die Produktion: Ja. Activepieces nutzt Redis als Queue für Background-Jobs. Ohne Redis (Memory Mode) verlierst du Tasks bei einem Neustart des Containers.

Absolut. Activepieces bietet Helm Charts an. Da es stateless ist (bis auf DB/Redis), skaliert es horizontal sehr gut.

Durch die Variable AP_ENGINE_EXECUTOR=SANDBOXED laufen Node-Skripte in isolierten Umgebungen. Das ist deutlich sicherer, als alles direkt auf dem Host-System auszuführen.

Ja. Du trägst sie einfach im 'package.json'-Tab der Code Node ein. Activepieces installiert sie automatisch zur Laufzeit in der Sandbox.

Tags:#Activepieces#n8n#Automation#Self-Hosted#Docker#Puppeteer
Artikel teilen:
RSS Feed

Bleib auf dem Laufenden mit neuen Artikeln. Abonniere den RSS Feed.

Gefällt dir dieser Artikel?

Entdecke mehr zum Thema Development.

Alle Artikel ansehen
Hendrik Haustein

Hendrik Haustein

Fullstack Developer

Hendrik ist Fachinformatiker für Anwendungsentwicklung, studiert Medieninformatik an der TH Lübeck und liebt sauberen Code und packende Stories. Er entwickelt diverse Web- und Mobile-Apps. Seit 2014 beschäftigt er sich mit Blogs und Content-Marketing.