Once a side project has users, the chores start: deploy on merge, sync payments into your database, send a welcome email, post a note somewhere when something breaks. Two tools come up constantly for this: GitHub Actions and n8n.
They overlap, and you can force either one to do almost anything. But they're built around different centers of gravity, and picking the right one per job saves a lot of friction.
The short version
- GitHub Actions is built around your repository. It shines when the trigger is a git event (push, pull request, tag, release) or the job needs your code checked out.
- n8n is built around moving data between services. It shines when the job is "when X happens in service A, do Y in services B and C", especially with SaaS APIs you don't want to write auth and pagination code for.
Most of the time the choice is obvious once you ask: does this job start from my code, or from someone else's service?
GitHub Actions: what it's good at
Workflows are YAML files in .github/workflows/. They run on GitHub-hosted runners (or your own) in response to events.
Good fits:
- tests and linting on every PR,
- building and deploying on push to
main, - publishing a package or release notes when you push a tag,
- scheduled jobs that need the repo, like regenerating a sitemap or updating a dependency report.
A minimal deploy-on-push workflow:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
- run: npm run build
- run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
What you get for free: no server to run, secrets management, logs for every run, and direct access to commit and branch context. Actions also supports scheduled workflows with cron syntax (on: schedule: - cron: '0 6 * * *'), with a few caveats covered below.
Where it gets awkward: anything that's mostly calling third-party APIs. You can do it with a script step, but you're writing and maintaining the API client, retries and error handling yourself.
Cost
At the time of writing, public repositories get GitHub-hosted runner minutes on standard runners for free. Private repositories draw from a monthly allowance (2,000 minutes on the Free plan, more on paid plans), then bill per minute. Check GitHub's current pricing before relying on this; it has changed before.
The detail that bites people: GitHub rounds each job up to the nearest whole minute. A job that runs for 5 seconds costs a full minute.
n8n: what it's good at
n8n is a workflow automation tool with a visual node editor. You chain triggers (webhook, schedule, an app event) with nodes for specific services (Stripe, Postgres, Slack, Notion, Gmail, GitHub and many more), and drop into JavaScript or Python when you need custom logic. You can self-host it (the source is available under n8n's Sustainable Use License) or use their paid cloud.
Good fits:
- a Stripe event creates or updates a row in your database,
- a new signup triggers a welcome email and a note in your CRM or Notion,
- a form submission lands in a spreadsheet and pings you,
- a daily digest pulls numbers from a few APIs and emails them to you.
For example, a "new paying customer" workflow might be:
-
Stripe Trigger on
checkout.session.completed - Postgres node: upsert the customer
- HTTP Request node: call your own API to provision their account
- Send Email (or Gmail) node: welcome message
- An error workflow that notifies you if any step fails
Each of those would be a chunk of code in an Actions script. In n8n the nodes handle credentials and API details, and you mostly write the data mapping.
n8n can also react to repo events: its GitHub Trigger node registers a webhook for things like pushes or new issues. So "n8n can't do git events" isn't true; it's just not where it's strongest, because it doesn't have your code checked out.
Cost
Self-hosting is free in license terms, but it's a server you now own: updates, backups of its database (which holds your workflows and credentials), HTTPS, and monitoring. For many indie developers that's a small VPS they already have. n8n Cloud removes the ops work for a monthly fee; plans and execution limits change, so check their pricing page.
The trap: frequent schedules on Actions
A pattern that looks cheap and isn't: using Actions as an uptime monitor with a cron every five minutes.
- Every 5 minutes is 288 runs a day, roughly 8,640 a month.
- Each run is billed as at least one minute.
- On a private repo, that's around 8,640 billed minutes a month, over four times the Free plan's allowance, for a job that runs
curlfor two seconds.
It's also not a great monitor. GitHub's docs note that scheduled workflows can be delayed during periods of high load, five minutes is the shortest interval allowed, and in public repositories scheduled workflows are automatically disabled after 60 days without repository activity.
For uptime checks, use a dedicated uptime monitoring service or a schedule trigger in n8n (which runs on your server, so frequency doesn't cost minutes). Keep Actions schedules for jobs that run a few times a day at most and genuinely need the repo.
When to use both
A split that works well: Actions owns everything up to "the new version is live", and n8n owns what happens after.
# last step of the deploy job
- name: Tell n8n about the deploy
if: success()
run: |
curl -fsS -X POST "$N8N_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"sha\": \"${GITHUB_SHA}\", \"ref\": \"${GITHUB_REF_NAME}\"}"
env:
N8N_WEBHOOK_URL: ${{ secrets.N8N_DEPLOY_WEBHOOK }}
The n8n workflow behind that webhook can post to Discord, update a status page, add a changelog row, and email beta users, without any of that logic living in your repo's CI config. If the n8n side fails, your deploy still succeeded, which is usually what you want.
If you add a webhook like this, protect it: at minimum a hard-to-guess URL kept in secrets, ideally a shared token checked in the workflow.
A quick way to decide
List the chores you currently do by hand and note two things for each: what triggers it, and what it touches.
| Chore | Trigger | Touches | Pick |
|---|---|---|---|
| Run tests on PRs | Pull request | Your code | Actions |
| Deploy on merge | Push to main
|
Your code, host | Actions |
| New Stripe customer -> DB + welcome email | Stripe event | Stripe, DB, email | n8n |
| Weekly metrics email | Schedule | Several APIs | n8n |
| Regenerate docs site | Push | Your code | Actions |
| Uptime check every few minutes | Schedule | Your site | Uptime monitor or n8n |
| Announce a release | Tag push | Discord, email, status page | Actions -> n8n webhook |
If a chore takes you five minutes a month, automating it with either tool may not be worth the maintenance. Automate the ones that are frequent, error-prone, or that you tend to forget.
Bottom line
Start with GitHub Actions, because you probably already have it and it covers everything code-shaped. Add n8n when you catch yourself writing API glue in YAML, or when a job has nothing to do with your repository. And be careful with frequent cron schedules on private repos; that's where Actions gets surprisingly expensive.
If you're running a few side projects and want one place to track them (ideas, launch checklist, revenue), I made a Side Project Tracker Notion template.
More templates and a free Dataview starter pack are at forge.engelailabs.com.
Top comments (0)