← Back to Blog
Automation7 min read

36 Lessons From Self-Hosting an Automation Project End to End

A field guide pulled from actually building and debugging a self-hosted Telegram bot, from account signups to the bugs nobody warns you about.

Jive Ian Bogwat

July 2, 2026

36 Lessons From Self-Hosting an Automation Project End to End

Most tutorials show you the happy path. Sign up here, click this, paste that, done. What they rarely show you is the host that won't resolve, the model that got deprecated mid-build, or the bug that returns a string instead of the file you asked for.

I kept notes on all of it while building a self-hosted expense tracker, a Telegram bot backed by n8n, Gemini, and a free Postgres database. What follows is the condensed version, 36 things worth knowing before you hit them yourself.


Account and Service Setup

The boring part, but worth doing right the first time.

  1. ngrok — sign up free, no card. Grab the permanent dev domain and authtoken from the dashboard immediately, both stay valid indefinitely.
  2. Telegram bot — message @BotFather, send /newbot, get a token. Tokens don't expire unless you manually revoke them.
  3. Google AI Studio (Gemini key) — free API key, no card. Ignore the "Set up billing" prompt, it's optional.
  4. Supabase — sign up free, GitHub or email works the same either way. Create an org, then a project, and save the database password immediately, since it's shown to you exactly once.

Local Infrastructure

This is where "free" starts to mean actual infrastructure decisions, not just a signup form.

  1. Install Docker Desktop, then enable "Start Docker Desktop when you sign in" under Settings → General.
  2. Write a docker-compose.yml with restart: unless-stopped, so containers heal themselves after a crash or a full reboot.
  3. docker compose up -d to start, docker compose down to stop, and docker compose down && docker compose up -d any time you need a config change to actually apply.
  4. ngrok http --url=yourdomain.ngrok-free.dev PORT tunnels a local port to a stable public URL.
  5. On Windows, auto-start ngrok through a .bat file dropped into the Startup folder (Win+R then shell:startup).

Database Setup, the Supabase-Specific Parts

The parts that a generic Postgres tutorial won't mention, because they're specific to how Supabase routes connections.

  1. Create the project, run your schema once through the SQL Editor. Don't hand-create tables through the UI, it doesn't scale past the first table.
  2. Get connection details through the green Connect button, not a buried Settings page.
  3. If you hit "host not found," it's because Supabase's direct connection is IPv6-only. Switch to the Session Pooler connection on port 5432 instead, which supports IPv4.
  4. If you then hit "self-signed certificate in certificate chain," set the credential's SSL mode to Allow and enable Ignore SSL Issues. Still encrypted, just less strict about the certificate chain, a known and safe workaround for a personal project.
  5. Session pooler usernames are formatted postgres.<project-ref>, not just postgres. Copy this straight from the Connect panel rather than assuming the format.

Building the Actual n8n Workflow

Where most of the real debugging time went.

  1. Import workflows through Create workflow → Start from scratch → the "..." menu → Import from file, not from the main Workflows list dropdown, which doesn't have that option.
  2. Every node showing a red warning triangle needs a credential assigned. Click in, pick from the dropdown, save.
  3. For APIs authenticated via a URL query parameter, like Gemini's ?key=, skip the saved-credential system entirely and paste the key directly into the node's URL field.
  4. Publish after every single node edit. Saving a node is not the same as the live, webhook-triggered workflow actually using that change.
  5. Use Execute step on a scheduled workflow to test it immediately, instead of waiting for the real schedule to fire.
  6. Branching nodes need an explicit fallback output enabled, or any input that doesn't match a defined rule silently goes nowhere, no error, nothing.
  7. Check the Executions tab after any failure. It shows exactly which node failed and the real error message, reading it beats guessing every time.

AI Integration Gotchas

The part that felt most like working with a living dependency rather than a fixed tool.

  1. Don't treat a specific model name as permanent infrastructure. Models get deprecated with little warning, I hit this mid-build when a model's free quota dropped to zero overnight.
  2. For structured extraction, spell everything out explicitly: exact JSON keys, the exact allowed values, and "no prose, no markdown fences, JSON only."
  3. For binary file handling, like converting a downloaded image to base64, use a platform's dedicated built-in node instead of hand-rolled code. A Code node's binary handling can have real, hard-to-diagnose sandbox bugs.

A Debugging Method Worth Repeating

Two habits that saved the most time, across every kind of failure.

  1. When an API call fails, read the full error detail panel, not just the one-line summary. The actual request body sent usually reveals the real problem in seconds.
  2. Isolate failures node by node using "Execute step," rather than re-running the whole chain and guessing from the end result.

Cost and Security Discipline

Non-negotiable if the goal is genuinely $0 with nothing sensitive exposed.

  1. Free tiers that require a card for "verification" are avoidable. Local hosting plus a free tunnel service covers the same need with zero card risk.
  2. Never let real credentials end up in exported files. Keep placeholders in anything you save or share, so a backup is never also a leak.
  3. Before pushing to a public repo, grep every file for key patterns, hostnames, and IDs. Don't assume it's clean just because it looks clean.
  4. Add a .gitignore before you think you need one. It's a cheap safety net that only works if it's in place before the risky file ever gets created.

Reliability and Production Readiness

The difference between a demo and something you'd actually trust to run unattended.

  1. restart: unless-stopped in Docker Compose, so containers recover automatically.
  2. Enable Docker's own auto-start on login.
  3. Auto-launch any companion process, like a tunnel, through the OS's own startup mechanism.
  4. Actually test durability with a real full restart. Don't assume auto-start settings work until you've watched them work.

Data Integrity, Learned Late

The two lessons that showed up only after everything else already looked finished.

  1. AI-based input parsing needs an explicit validity check, like a boolean flag returned from the model, or it will confidently generate structured garbage from meaningless input, and your pipeline will happily save it as if it were real.
  2. Keep "what the AI is allowed to output" and "what the system tracks against" documented as a pair. They will quietly drift apart otherwise, and neither half of that drift throws an error.

The Actual Takeaway

None of these are exotic. Each one is a small, specific thing that cost real time the first time it happened and costs nothing the second time, because now it's just a checklist. That's the whole case for writing this down instead of trusting memory for the next project.

Tags

n8nautomationdockerself-hosteddebugging