Architecture

    Deployment

    How ProcessFlow is built, published, and deployed using GitHub Actions, Docker Hub, Traefik, and Watchtower.

    Deployment Overview

    A concise guide to the CI/CD pipeline, hosting architecture, and deployment workflow for ProcessFlow, from code commit to a running production environment.


    CI/CD Pipeline (GitHub Actions)

    1. Trigger: On push to main or manual dispatch.
    2. Build & Publish:
      • Checkout code
      • Set up Docker Buildx
      • Authenticate to Docker Hub
      • Build the Next.js Docker image
      • Push mertend/process-flow:latest to Docker Hub

    Database migrations are not run in CI. They run automatically on container startup via docker-entrypoint.sh (prisma migrate deploy), so every new deployment picks up pending migrations before serving traffic.


    Hosting Components

    ComponentRole
    process-flowThe Next.js application container
    process-flow-dbSelf-hosted PostgreSQL 17 container with a named volume for data persistence
    activity-openrouterSidecar container providing the built-in OpenRouter custom activity
    Docker HubImage registry for mertend/process-flow:latest
    Ubuntu ServerHetzner VPS running Docker Compose, Traefik, and Watchtower

    Server Deployment (Docker Compose)

    The Ubuntu host uses docker-compose.yaml:

    services:
      process-flow:
        image: mertend/process-flow:latest
        container_name: merten-process-flow
        restart: unless-stopped
        networks:
          - web
        depends_on:
          - process-flow-db
        environment:
          DATABASE_URL: postgresql://processflow:${DB_PASSWORD}@process-flow-db:5432/processflow
          BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
          BETTER_AUTH_URL: https://processflow.merten.tech
          APP_URL: https://processflow.merten.tech
          NEXT_PUBLIC_APP_URL: https://processflow.merten.tech
          ACTIVITY_OPENROUTER_URL: https://processflow.merten.tech/activities/openrouter/call
        labels:
          - traefik.enable=true
          - traefik.http.routers.process-flow.rule=Host(`processflow.merten.tech`) && !PathPrefix(`/activities`)
          - traefik.http.routers.process-flow.entrypoints=websecure
          - traefik.http.routers.process-flow.tls.certresolver=le-merten
          - traefik.http.services.process-flow.loadbalancer.server.port=3000
     
      activity-openrouter:
        image: mertend/activity-openrouter:latest
        container_name: merten-activity-openrouter
        restart: unless-stopped
        networks:
          - web
        labels:
          - traefik.enable=true
          - traefik.http.routers.activity-openrouter.rule=Host(`processflow.merten.tech`) && PathPrefix(`/activities/openrouter`)
          - traefik.http.routers.activity-openrouter.entrypoints=websecure
          - traefik.http.routers.activity-openrouter.tls.certresolver=le-merten
          - traefik.http.services.activity-openrouter.loadbalancer.server.port=3000
     
      process-flow-db:
        image: postgres:17-alpine
        container_name: merten-process-flow-db
        restart: unless-stopped
        networks:
          - web
        environment:
          POSTGRES_DB: processflow
          POSTGRES_USER: processflow
          POSTGRES_PASSWORD: ${DB_PASSWORD}
        volumes:
          - process-flow-db-data:/var/lib/postgresql/data
     
    volumes:
      process-flow-db-data:
     
    networks:
      web:
        external: true

    Key points:

    • process-flow-db runs PostgreSQL 17 in the same Docker network. Data is persisted in a named volume.
    • depends_on ensures the DB container is running before the app starts.
    • activity-openrouter is served under /activities/openrouter on the same domain via Traefik path-prefix routing.
    • The app container's entrypoint runs prisma migrate deploy + seeding before starting the Next.js server.

    Database Migrations

    Migrations are managed with Prisma Migrate:

    • Schema changes: npx prisma migrate dev (creates a new migration file locally)
    • Production: migrations run automatically on every container startup via docker-entrypoint.sh

    No manual migration step is needed after a deployment — Watchtower pulls the new image and restarts the container, which applies any pending migrations on boot.


    Automatic Updates (Watchtower)

    A Watchtower container running on the Ubuntu server:

    • Continuously checks Docker Hub for new :latest tags.
    • Stops the running process-flow container.
    • Pulls the updated image and restarts it with the existing environment config.
    • Ensures minimal downtime when combined with Traefik's load balancing.

    Traffic Routing (Traefik)

    • Entrypoint: websecure (HTTPS port 443).
    • TLS: Managed by Let's Encrypt via the le-merten cert resolver.
    • Router Rule: processflow.merten.tech — app traffic excludes /activities/* paths which are routed to activity sidecar containers.
    • Network: Attached to the external web network for service discovery.

    This setup provides a fully automated path from code to production — Watchtower handles rolling updates, Prisma handles migrations, and Traefik handles SSL and routing.

    On this page

    Deployment