DEV Community

Cover image for I Replaced a $300/month Automation Stack with n8n, FastAPI, and Docker
Muhammad Gharis
Muhammad Gharis

Posted on

I Replaced a $300/month Automation Stack with n8n, FastAPI, and Docker

Most small business automation stacks start simple.

A form submission goes into a CRM.

A lead gets scored.

An email or WhatsApp message gets sent.

Maybe an invoice gets extracted with AI.

Then one day, the stack looks like this:

Tool Monthly Cost Purpose
Zapier Professional $49 Workflow automation
Make Core $29 Secondary automation
OpenAI API $60+ AI processing
HubSpot Starter $50 CRM
Typeform $25 Form intake
Airtable Plus $20 Data storage
Webhook testing / API tools $20+ Debugging and routing
Extra automation tools $30+ Glue logic
Total $280–$300+/month Fragmented automation stack

The bigger problem is not only the cost.

The bigger problem is fragmentation.

Zapier triggers Make.

Make calls an AI API.

The AI response gets written to Airtable.

A separate sync pushes data into the CRM.

Errors happen silently.

Duplicate records appear.

Nobody knows where the source of truth lives.

I built omni-odoo-stack to explore a cleaner alternative:

A self-hosted, open-source-friendly automation architecture using:

  • n8n
  • FastAPI
  • Docker
  • PostgreSQL
  • Odoo 19 Community
  • Groq / Llama 3
  • WhatsApp Cloud API
  • Nginx

The goal was to show how a business automation stack can be designed with fewer moving parts, better validation, and lower infrastructure cost.

Repository:

github.com/gharisj3/omni-odoo-stack


The Replacement Stack

Paid / Fragmented Tool Replacement Cost
Zapier / Make / Pipedream n8n self-hosted $0
Raw AI API calls FastAPI + Pydantic validation $0
HubSpot CRM Odoo 19 Community CRM $0
Airtable PostgreSQL $0
Typeform Custom webhook endpoint $0
Webhook testing tools n8n webhook trigger + logs $0
VPS hosting Oracle OCI Always Free path $0

The architecture is designed to run on a single Docker Compose stack.

The exact cloud cost depends on provider limits and usage, but the project is designed around a $0/month infrastructure path using open-source and free-tier-friendly components.


The Architecture

At a high level, the system looks like this:

Inbound trigger
Webhook / form / WhatsApp / API
        ↓
n8n
Workflow orchestration
        ↓
FastAPI middleware
AI processing + validation
        ↓
Groq / Llama 3
LLM intelligence layer
        ↓
PostgreSQL
Data layer
        ↓
Odoo 19 Community
CRM, accounting, inventory, HR
        ↓
Outbound action
WhatsApp reply / email / record update
Enter fullscreen mode Exit fullscreen mode

The most important design decision is separation.

n8n handles orchestration.

FastAPI handles AI and validation.

Odoo handles ERP records.

PostgreSQL stores the data.

WhatsApp handles communication.

No single tool is forced to do everything.


Why n8n Beats Zapier and Make for Technical Users

Zapier and Make are excellent products.

If you are non-technical and need something running quickly, they are useful.

But for developers, agencies, and technical teams, self-hosted n8n gives more control.


Problem 1: Per-Execution Pricing Adds Up

Zapier charges by tasks.

Make charges by operations.

That works for small flows.

But once you start processing thousands of records, invoices, leads, or messages, pricing can grow quickly.

Self-hosted n8n removes per-execution platform pricing.

You still pay for server resources, API calls, and external services, but the workflow engine itself is under your control.


Problem 2: Error Handling Needs to Be First-Class

In real workflows, things fail.

APIs timeout.

AI returns malformed JSON.

A CRM rejects a payload.

A webhook gets sent twice.

A database write fails.

A production workflow needs:

  • retries
  • validation
  • logging
  • duplicate prevention
  • alerting
  • fallback paths

n8n gives you enough flexibility to route failures into dedicated error workflows, log them, and notify the right person.

That matters more than people think.


Problem 3: Your Data Flow Should Be Understandable

In many automation stacks, the logic is spread across five tools.

One part lives in Zapier.

Another part lives in Make.

Another part lives in Airtable.

Another part lives inside the CRM.

Another part lives in an AI prompt.

When something breaks, debugging becomes painful.

A self-hosted stack lets you keep the workflow, API layer, database, and ERP integration closer together.

That makes the system easier to reason about.


Why FastAPI Instead of Calling AI Directly from n8n

The biggest mistake I see in AI automation stacks is this:

Call an LLM directly from a workflow tool and write the raw response into a business system.

That is dangerous.

Example of what not to do:

# Dangerous pattern
response = openai.chat.completions.create(...)

db.insert(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

The LLM can return anything.

Wrong fields.

Wrong format.

Wrong amounts.

Wrong dates.

Invalid JSON.

Hallucinated values.

If that response goes directly into a CRM, ERP, or accounting system, you are asking for corrupted business data.

So I added a FastAPI middleware layer between the workflow and the AI model.

The middleware validates AI output before anything touches Odoo or PostgreSQL.


Pydantic Validation Layer

Here is a simplified example using Pydantic v2:

from typing import Literal
from datetime import date
from pydantic import BaseModel, Field


class LeadAnalysis(BaseModel):
    score: int = Field(ge=1, le=10)
    summary: str = Field(min_length=10, max_length=500)
    next_action: str
    priority: Literal["low", "medium", "high"]


class InvoiceData(BaseModel):
    vendor: str
    amount: float = Field(gt=0)
    date: date
    currency: str = Field(min_length=3, max_length=3)
    line_items: list[dict]
Enter fullscreen mode Exit fullscreen mode

If the AI response does not match the schema, the middleware rejects it.

That means bad data never reaches the ERP.


FastAPI Endpoint Example

from fastapi import FastAPI, HTTPException, Header
from pydantic import ValidationError
import json

app = FastAPI()


@app.post("/analyze/lead", response_model=LeadAnalysis)
async def analyze_lead(lead: dict, x_api_key: str = Header(...)):
    if x_api_key != API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API key")

    response = client.chat.completions.create(
        model="llama3-8b-8192",
        messages=[
            {
                "role": "user",
                "content": (
                    "Analyze this lead and respond only with valid JSON "
                    f"matching this schema: {LeadAnalysis.model_json_schema()}. "
                    f"Lead data: {lead}"
                ),
            }
        ],
        response_format={"type": "json_object"},
    )

    try:
        raw = json.loads(response.choices[0].message.content)
        return LeadAnalysis(**raw)
    except (json.JSONDecodeError, ValidationError) as exc:
        raise HTTPException(status_code=422, detail=str(exc))
Enter fullscreen mode Exit fullscreen mode

This is the safety layer.

n8n calls FastAPI.

FastAPI calls the AI model.

Pydantic validates the response.

Only validated data is returned to the workflow.


Docker Compose Setup

One Docker Compose file can run the core stack:

version: "3.8"

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - omni_net

  odoo:
    image: odoo:19
    depends_on:
      - db
    environment:
      HOST: db
      USER: ${POSTGRES_USER}
      PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - odoo_data:/var/lib/odoo
      - ./odoo/addons:/mnt/extra-addons
      - ./odoo/config/odoo.conf:/etc/odoo/odoo.conf
    networks:
      - omni_net

  ai-middleware:
    build: ./ai
    environment:
      GROQ_API_KEY: ${GROQ_API_KEY}
      API_KEY: ${AI_API_KEY}
    networks:
      - omni_net

  n8n:
    image: n8nio/n8n
    environment:
      N8N_BASIC_AUTH_ACTIVE: "true"
      N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
      N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - omni_net

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - odoo
      - n8n
      - ai-middleware
    networks:
      - omni_net

volumes:
  postgres_data:
  odoo_data:
  n8n_data:

networks:
  omni_net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Startup flow:

cp .env.example .env

# Fill in your environment variables

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Four main services.

One network.

One command.


Workflow 1: Lead Intake

This replaces a typical form + automation + CRM workflow.

WhatsApp message / web form submission
        ↓
n8n webhook trigger
        ↓
HTTP Request → POST /analyze/lead
        ↓
Validate AI response
        ↓
HTTP Request → POST /api/omni/lead
        ↓
Create Odoo CRM lead
        ↓
Send WhatsApp confirmation
        ↓
Log automation event
Enter fullscreen mode Exit fullscreen mode

Old pattern:

Form tool → Zapier → AI API → HubSpot → notification

New pattern:

Webhook → n8n → FastAPI → Odoo → WhatsApp


Workflow 2: Invoice Processing

Invoice text or payload arrives
        ↓
n8n webhook trigger
        ↓
HTTP Request → POST /extract/invoice
        ↓
Pydantic validation
        ↓
Create vendor bill in Odoo
        ↓
Send confirmation
        ↓
Log validation failures if needed
Enter fullscreen mode Exit fullscreen mode

The key point is not AI extraction.

The key point is validated AI extraction.

A malformed invoice should never become an accounting record.


Workflow 3: HR Onboarding

New employee form submitted
        ↓
n8n webhook trigger
        ↓
Create employee in Odoo HR
        ↓
Generate welcome message through FastAPI
        ↓
Send WhatsApp welcome message
        ↓
Notify HR manager
        ↓
Log onboarding event
Enter fullscreen mode Exit fullscreen mode

This shows that the same stack can support sales, finance, and HR workflows.


Error Handling Pattern

The single biggest problem with many automation stacks is silent failure.

A workflow fails.

Nobody notices.

Leads disappear.

Invoices are not processed.

Customers never get replies.

Every serious workflow should have an error path.

Every important workflow step
        ↓
Success path → continue normally
        ↓
Error path → log failure
           → notify admin
           → stop workflow safely
Enter fullscreen mode Exit fullscreen mode

In this stack, failures can be logged into Odoo using omni.automation.log.

They can also trigger WhatsApp or email alerts.

That gives the business visibility into what failed and why.


Five Gotchas That Will Save You Hours

1. n8n's Odoo Node Is Not Enough for Every Case

The built-in Odoo node is useful for standard operations.

But for custom Odoo REST controllers, HTTP Request nodes are often better.

They give you more control over:

  • authentication
  • headers
  • custom endpoints
  • error responses
  • payload shape

2. Webhook Idempotency Is Not Optional

Webhook providers may retry failed events.

If you do not check for duplicate message IDs or event IDs, you can create duplicate records.

For lead intake, that means duplicate CRM leads.

For accounting, that could mean duplicate bills.

Always store an external event ID and check it before creating new records.


3. AI Rate Limits Need Workflow Design

Free-tier AI APIs can have rate limits.

Do not assume every request will succeed immediately.

For high-volume workflows, add:

  • wait nodes
  • retries
  • backoff
  • failure logging
  • fallback behavior

Rate limits should be treated as part of the architecture, not as an afterthought.


4. Odoo Filestore Persistence Is Easy to Miss

Odoo stores attachments in its filestore.

If you do not mount the filestore properly in Docker, files can disappear when containers are recreated.

Use named volumes for persistence.


5. Docker Services Should Talk by Service Name

Inside a Docker Compose network, services should not call each other using localhost.

Use service names instead:

http://odoo:8069
http://ai-middleware:8000
http://n8n:5678
Enter fullscreen mode Exit fullscreen mode

localhost points to the current container, not another service.

This causes many avoidable connection errors.


Before and After

Metric Before After
Monthly platform cost Around $300 Designed for $0/month infrastructure path
Services to maintain Many separate tools One Docker Compose stack
Workflow ownership Spread across vendors Self-hosted orchestration
Error visibility Often limited Central logs and alerts
Data model Fragmented PostgreSQL + Odoo
AI safety Raw responses Pydantic validation
Vendor lock-in High Lower

Important note:

This does not mean there are no external services.

WhatsApp Cloud API and Groq are still external APIs.

The difference is that the workflow engine, database, ERP, middleware, and deployment structure are controlled by you.


Full Repository

Everything is open source and available here:

github.com/gharisj3/omni-odoo-stack

The repo includes:

  • Docker Compose setup
  • Odoo configuration
  • Custom Odoo modules
  • FastAPI middleware
  • Pydantic v2 validators
  • Importable n8n workflow JSON files
  • Documentation
  • Setup notes
  • Architecture notes

Final Thought

The point of this project is not just to replace paid tools.

The point is to show that business automation needs real backend architecture.

A good automation stack needs:

  • validation
  • observability
  • retry handling
  • duplicate prevention
  • API boundaries
  • secure configuration
  • persistent storage
  • clean deployment

That is what turns a demo workflow into something a business can actually rely on.


About Me

I am a backend engineer specializing in:

  • Odoo ERP customization
  • n8n workflow automation
  • Python backend systems
  • FastAPI middleware
  • PostgreSQL optimization
  • WhatsApp Cloud API integration
  • AI middleware with structured output validation

I have 8+ years of production experience across Odoo, Python, backend automation, and ERP integrations.

I am open to remote contract work involving Odoo modules, n8n automation, AI middleware, PostgreSQL optimization, and backend integrations.

GitHub:

github.com/gharisj3

LinkedIn:

linkedin.com/in/muhammad-gharis-javed-318266202

Project repo:

github.com/gharisj3/omni-odoo-stack

Top comments (0)