GitHub Actions vs GitLab CI vs Drone: which CI/CD to pick for a Brazilian startup
GitHub Actions won mindshare but has minute costs. GitLab CI is more complete but heavier. Drone (and Woodpecker) self-hosted runs on a small VPS. Practical comparison.
The CI/CD choice in 2026 is no longer about "which tool has more features". All three serious ones — GitHub Actions, GitLab CI, Drone (and its fork Woodpecker) — do the basics well. The real choice is about where your pain is going to show up first: on the bill at the end of the month, on workflow complexity when the monorepo grows, or when you have to bring up a runner nobody understands when the senior dev goes on vacation.
This post is an honest comparison for Brazilian tech leads deciding CI/CD in 2026. No artificial ranking, no column where one tool is "champion" at everything. Explicit tradeoffs, numbers in reais, and a recommendation per profile at the end.
TL;DR (200 words)
The CI/CD decision in 2026 follows four forces: where the code is hosted, minute cost, workflow complexity, and willingness to operate self-hosted.
GitHub Actions won absolute mindshare for projects on GitHub. It's free up to 2000 minutes/month on public repos; after that costs US$0.008/min on a Linux runner — between US$5 and US$30/month for a typical startup (R$25 to R$150). Marketplace has 10 thousand ready actions. The Achilles heel is the minute pricing when volume grows.
GitLab CI is more complete: native job dependency graph, parent-child pipelines, better monorepo handling, included image registry, embedded security scanning. Self-hosted (Community Edition) is free but requires 4 to 8 GB of RAM and active operation. SaaS Premium is US$29/user/month — expensive for a large team.
Drone/Woodpecker self-hosted is the option for cutting variable cost to zero. A R$30 to R$80/month server runs CI for five to ten projects. Costs in ops: you operate the runners.
For a small BR startup on GitHub, start on the Actions free plan. When it passes US$30/month, consider Woodpecker self-hosted. For a company that values CI + issue tracker + registry in a single product, GitLab self-hosted.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Why does this decision matter more than it seems?
CI/CD is the most-used infrastructure of any product team: every commit touches the system, every PR depends on it, every deploy goes through it. A wrong choice doesn't break you in the first month — it breaks you in the third year, when migrating costs four weeks of two people and you pay that while the roadmap stalls.
The three symptoms that indicate the choice was wrong:
- The bill grows faster than the team. If CI cost doubles every six months without deploy volume justifying it, the pricing model isn't yours.
- Workflows become copy-paste. If every new project starts with
cp -r .github/workflows/, the tool has no decent composition. - CI failures take more than an hour to debug. If reproducing the error locally requires running a Docker image nobody knows how to set up, the build isn't portable.
The three main competitors solve these symptoms in different ways. Let's go piece by piece.
GitHub Actions: the de facto standard — is it worth the price?
If your code is on GitHub, Actions has a structural advantage that can't be ignored: zero integration friction. You create .github/workflows/ci.yml, push, and you're running. No separate signup, no cross token, no webhook to configure.
What Actions does well
- Huge marketplace. More than ten thousand ready actions for common tasks: setup of Node, Python, Go; deploy to AWS, GCP, Azure; image signing; security scanning. Most are maintained by the technology vendor itself (HashiCorp publishes its own, AWS publishes its own, etc).
- Matrix builds. Running the same suite against five Node versions or three operating systems is a key in three lines.
- Reusable workflows. Since 2021 you can extract workflows shared across repos in the same organization — solves the "copy-paste between projects" problem for medium teams.
- Deployment protection rules. Manual approvals, time windows, branch restriction — all configurable without plugin.
- Self-hosted runners. You can run the agent on your own infra and use the Actions UI just as orchestrator. Solves the minute problem for high-volume teams.
What Actions charges dearly for
The billing model is per minute, and the numbers matter:
| Runner type | Cost |
|---|---|
| Linux 2 vCPU (default) | US$0.008/min (R$0.04) |
| Windows 2 vCPU | US$0.016/min (R$0.08) |
| macOS (required for iOS builds) | US$0.08/min (R$0.40) |
| Larger Linux (4 vCPU+) | US$0.016/min and above |
For a startup on GitHub with five devs and a reasonable workflow (build + test + lint on each PR), typical consumption is 800 to 2500 minutes/month on Linux. That's US$6 to US$20/month — that is, between R$30 and R$100. Fits in the "dev tools" line without pain.
When it hurts: heavy workflows (E2E with Playwright, Rust builds, tests that bring up Postgres + Redis on each job) easily pass 10 thousand minutes/month. At US$0.008/min that becomes US$80/month — R$400. Multiply by 12 and you're paying R$5 thousand/year on CI.
macOS builds are the worst case: US$0.40/min is ten times more than Linux. Teams maintaining iOS apps spend three to four times more on CI than on production infra.
Do Actions self-hosted runners solve it?
Partially. You run the runner binary on a machine of yours, register on the repo or organization, and jobs go there instead of the managed pool. Minute cost goes to zero — you only pay for the machine.
But three catches:
- Runner maintenance. The version updates frequently; outdated runners start failing silently. Without automation, it becomes manual operation work.
- Manual scaling. If the team has five devs opening 20 simultaneous PRs, one runner serializes everything. You need N runners — and provisioning/deprovisioning on demand requires additional tooling.
- Security on public repos. Self-hosted runners on a public repo are an open door for any malicious fork to run arbitrary code on your machine. Always restricted to private repos or trusted organizations.
The mature solution is Actions Runner Controller (ARC): an operator that brings up runners on-demand on a Kubernetes cluster or similar. Solves scaling, but adds an entire infrastructure layer — not trivial.
GitLab CI: does the "heavyweight" competitor still make sense?
GitLab CI is older than Actions, more complete in features, and less popular outside teams already on the GitLab platform. The right question isn't "is GitLab CI better than Actions?", it's "is it worth migrating to GitLab to use GitLab CI?"
What GitLab CI does better
- Dependency graph (DAG). Native, without external tooling. You declare
needs: [job_a, job_b]and jobs run in parallel respecting dependencies. For workflows with 30+ jobs (large monorepo, multiple languages, multi-environment deploy), this is the difference between 8 minutes and 25 minutes per pipeline. - Parent-child pipelines. A large pipeline can trigger child pipelines with conditional logic — useful for monorepos where only changed services need to build.
- Included image registry. Each project comes with a native private container registry. No configuring secrets for Amazon ECR, Docker Hub, or similar.
- Pages, security scanning, code quality, dependency scanning — all embedded in the platform. In Actions each is a separate marketplace action.
- Deep merge request integration. Pipelines appear inside the MR with coverage diff, bundle size comparison, build time comparison. In Actions checks appear as links — in GitLab they're structured data.
Where GitLab CI charges dearly
Two dimensions.
SaaS pricing:
| Plan | Cost | Monthly minute limit |
|---|---|---|
| Free | US$0/user | 400 minutes |
| Premium | US$29/user/month (R$145) | 10,000 minutes |
| Ultimate | US$99/user/month (R$495) | 50,000 minutes |
For a five-dev team on Premium, that's US$145/month — R$725. That's just the entry ticket; extra minutes cost separately. For a team of 20, US$580/month = R$2,900 just on subscription.
Self-hosted Community Edition is free and removes that license cost — but:
- Realistic minimum: 4 vCPU, 8 GB RAM (16 GB if you'll use registry + pages + scanning).
- Adequate VPS in Brazil: R$120 to R$250/month.
- Ops: 2 to 4 hours/month on updates, backup, monitoring.
- Monthly updates. GitLab has a rigid cadence; staying three versions behind opens documented security holes.
In real production, self-hosted GitLab is less work than Kubernetes but more than Actions SaaS. It's a real server that you operate.
Drone CI and Woodpecker: the minimalist alternative
Drone CI was born in 2014 as the "container-native CI": each pipeline step is a container, no magic. In 2020 the company behind it (Drone Inc.) was acquired by Harness, and the product gained a commercial Cloud version. The community fork Woodpecker remains 100% open-source, with API compatible with Drone.
What Drone/Woodpecker does well
- Simple YAML. Each step declares an image and a command. No DSL, no reusable actions with their own semantics. What you run locally with
docker runis what runs on CI. - Container-native. There's no Java "executor", no Python agent running steps. Each step is an isolated container. Reproducing the error locally is literal: copy the
image:andcommands:from YAML and run in the terminal. - Self-hosted from day one. There's no "free Drone Cloud" pulling features into the paid version. The server + runners are the whole product.
- Plugins via container. Each plugin (SSH deploy, Slack, Docker push, AWS) is a published image. Versioned like any other dependency.
- Supports multiple code hosts. GitHub, GitLab, Bitbucket, Gitea, Forgejo — all on the same Drone server.
Where Drone/Woodpecker charges
- Smaller community. When you hit an obscure bug, Stack Overflow has five answers, not fifty. Github issues are your main source.
- Non-trivial operation at scale. One server + one runner is easy. Five autoscaling runners behind a queue is tooling you assemble — auto-scaling isn't built-in.
- Drone Cloud is paid. If you want SaaS, you go to Harness; the free tier is limited. That's why the recommendation is always self-hosted.
- Modest documentation. Covers the happy path; edge cases you discover by reading code.
Why Woodpecker instead of Drone in 2026
Vanilla Drone still works, but Harness has prioritized the commercial cloud version. Woodpecker is the community fork of original Drone — 100% open-source, no paid version pulling features, monthly active releases, engaged community. API and YAML compatible with Drone, so migration is trivial: swap the server URL.
For any small team self-hosting in 2026, Woodpecker is the better choice than vanilla Drone. Same architecture, without the overhead of a company controlling the roadmap.
Which is cheaper in 2026?
Real total monthly cost, considering a five-dev team with medium volume (300 builds/month, average 8-minute Linux builds):
| Option | Fixed cost | Variable cost | Estimated total/month |
|---|---|---|---|
| Woodpecker self-hosted (VPS R$80) | R$80 | R$0 | R$80 |
| Actions public repos (open-source) | R$0 | R$0 | R$0 |
| Actions private repos (free tier 2000 min) | R$0 | R$0 to R$50 | R$0 to R$50 |
| Actions Linux paid (medium volume) | R$0 | R$50 to R$150 | R$50 to R$150 |
| GitLab CI self-hosted (VPS R$200) | R$200 | R$0 | R$200 |
| Actions with heavy macOS builds | R$0 | R$300 to R$1,500 | R$300 to R$1,500 |
| GitLab CI SaaS Premium (5 devs) | R$725 | R$0 to R$200 | R$725 to R$925 |
Absolute cost winner: Woodpecker self-hosted for a team willing to operate a VPS. Costs the same as a lunch per month and runs CI for ten projects without breaking a sweat.
If ops isn't available, the Actions free plan is the next option. It fits a small team with light workflows; when it goes past US$30/month variable, it's worth at least evaluating self-hosted runners.
Which has the best developer experience?
DX in CI/CD is measured in three dimensions: time from "blank yml" to "first passing build", debug capability when it goes wrong, and ability to evolve the workflow when it grows.
| Dimension | Winner |
|---|---|
| Ready templates / accessibility | GitHub Actions (marketplace + onboarding) |
| Complex workflows / DAG / monorepo | GitLab CI (parent-child + native needs) |
| Local reproduction / conceptual simplicity | Drone/Woodpecker (each step = container) |
| Intermittent failure debug | Drone/Woodpecker (re-running an isolated step is trivial) |
| Cross-project composition | GitHub Actions (reusable workflows + composite actions) |
| Time-to-first-pipeline (zero to hello world) | GitHub Actions |
There's no absolute winner. For a team that values starting fast, Actions. For a team with complex workflow from day one (monorepo, multiple languages), GitLab CI. For a team that wants to understand exactly what's happening, Drone/Woodpecker.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Comparative table: 12 honest criteria
| Criterion | GitHub Actions | GitLab CI | Drone/Woodpecker |
|---|---|---|---|
| BR startup monthly cost (5 devs, medium volume) | R$0 to R$150 | R$80 to R$925 | R$80 |
| Real free tier (2026) | 2000 min/month private, unlimited public | 400 min/month SaaS | Unlimited self-hosted |
| Self-hosted available | Yes (runners), SaaS UI | Yes (full CE) | Yes (the only sensible way) |
| Large workflow complexity | Good (reusable workflows) | Excellent (DAG + parent-child) | Modest (linear + matrix) |
| Monorepo support | Medium (paths filter) | Excellent (rules + parent-child) | Medium (when filter) |
| Integrated container registry | No (needs separate GHCR) | Yes, native | No (use external registry) |
| Secret management | Repo + org + environment | Project + group + instance | Server + repo |
| Out-of-the-box parallel jobs | Yes (matrix) | Yes (parallel + DAG) | Yes (depends_on) |
| BR community / Portuguese material | Large | Medium | Small |
| PT-BR documentation | Partial (official in English) | Partial | Practically zero |
| GitHub/GitLab/Gitea integration | GitHub only | GitLab only (external mirror is workaround) | All three + Bitbucket |
| Ideal usage range | 1 to 50 devs on GitHub | 5 to 500 devs on a single platform | 1 to 30 devs with ops available |
No competitor has a column without caveats. The right tool depends on the team profile.
Decision by team profile
Four concrete recommendations, no "depends".
Indie hacker or public repo on GitHub
Use GitHub Actions free plan. Public repos have unlimited minutes. You have no reason to look for an alternative. If a year from now the project grows, you reassess.
Early startup on GitHub, private repos, R$10k to R$50k MRR
Stay on the Actions free plan. The 2000-minute free tier fits a two- to three-dev team with reasonable workflows. When it starts going over, first reduce waste (paths filter to not run everything on every PR, decent dependency cache) before migrating.
If you consistently go over US$30/month variable, consider migrating to self-hosted runners or Woodpecker in parallel.
Startup with R$50k to R$200k MRR on GitHub, high CI volume
Hybrid. Use Actions for light workflows (lint, unit tests) and self-hosted runners (via ARC) or Woodpecker for heavy workflows (E2E, long builds, deploys). You pay per minute where it pays off and zero where it hurts.
For teams with regular macOS builds, consider a dedicated Mac mini as a self-hosted runner. R$10 thousand investment pays off in three months if you spend US$200/month on macOS Actions today.
BR company on self-hosted GitLab
Use native GitLab CI. You're already paying the cost of operating GitLab; CI comes along at no additional cost. Migrating to another tool would mean operating two systems in parallel — not worth it.
Small team aggressively controlling cost
Woodpecker self-hosted on R$80 VPS. Runs CI for ten projects without sweating. Costs in ops 1 to 2 hours/month. If the team has someone with affinity for Unix tools, it's the most economical and most predictable option in the bill — you know exactly the cost every month.
Where HeroCtl comes in as runner infrastructure
Self-hosted CI/CD is exactly the type of workload that HeroCtl orchestrates well: long services (CI server, database that maintains build history), services that scale horizontally (runners that go up and down with the queue), services with persistence needs (artifact cache).
Instead of operating Docker Compose on a single server — single point of failure — you describe the setup as a job configuration:
- Drone/Woodpecker server as a long job, with a single replica and persistent volume for the history database.
- N runners as a replicable job, scaling horizontally. The orchestrator distributes the runners across nodes; if a server dies, the runners migrate to the others.
- Integrated backup for CI state (server database + artifact cache), without setting up external tooling.
- Integrated metrics and logs — you see CPU, memory, build time usage without bringing up a separate observability stack.
Practical difference: instead of operating a CI stack in parallel to your production cluster, it becomes part of the same cluster, with the same high-availability guarantees. If a server falls, the runners migrate. If you want to double capacity for a heavy sprint, change replicas: 4 to replicas: 8 in the configuration file.
For those on the "starting simple but going to grow" frontier, this solves the transition without needing to swap tools mid-path.
The 4 expensive errors in self-hosted CI/CD (and how to avoid them)
Error 1: silent stale cache
The symptom: build passes locally, fails on CI because of a dependency that exists on the dev machine but not on the fresh image. Worst case: passes on CI too because previous cache contains the dependency, but fails in production when the image is built without cache.
The fix: a decent cache assumes it can be invalidated at any moment. Whenever you change dependency manifest files (package.json, go.mod, requirements.txt, Cargo.toml), include them in the cache key. Periodically (weekly), force build without cache to detect drift.
Error 2: secret committed by accident
The symptom: someone pasted a token in the CI config "just to test", committed, forgot. The repo is public; in 12 hours the token is in use by someone who shouldn't.
The fix: two layered mechanisms. Pre-commit hook that scans for common key patterns (AWS, Stripe, GitHub PAT). Automatic rotation of critical tokens (90 days max). If a token leaks, the exposure window is finite.
In GitLab CI, use variables with "masked" and "protected" flags. In Actions, use environment-scoped secrets with approval rules. In Drone/Woodpecker, secrets are scoped per repo and never appear in logs by default.
Error 3: runner running on the same production server
The symptom: heavy build consumes CPU/RAM, production gets slow, latency rises, alarm goes off, on-call wakes up. Common real case in small teams trying to save a machine.
The fix: runners on a separate server from production, always. If the budget is tight, a runner on a R$30/month VPS is still cheaper than a production incident during business hours.
Error 4: workflow that doesn't run outside CI
The symptom: the CI build is a 200-line script inline in the YAML, with 15 environment variables that the system injects. When something goes wrong, nobody can reproduce locally without reverse-engineering the YAML.
The fix: CI should call commands that exist as Makefile, script/build, or package.json scripts. The CI YAML orchestrates; the logic lives in versioned scripts that run in any terminal. If you can't run make ci locally and see the same result, your CI isn't portable.
Drone/Woodpecker forces this discipline by design (each step is a container). Actions and GitLab CI allow the anti-pattern; it's up to the team to avoid it.
Frequently asked questions
Is GitHub Actions faster than Drone?
In raw build, depends on the runner: the Actions managed pool uses 2-vCPU machines; a self-hosted runner on a 4-vCPU machine is faster. In total pipeline time (including queue), Actions wins when there's volume — they have huge idle capacity. Self-hosted (any tool) has queue proportional to the number of runners you provision.
Can I use GitLab CI with a repo on GitHub?
Technically yes, via "pull mirror" (GitLab mirrors GitHub and runs CI on it). In practice it's fragile: webhooks lag, status checks don't return to GitHub the way the team expects, MRs get confusing. Not worth it. If you're on GitHub, use Actions or Drone/Woodpecker (which accept GitHub as a native source).
Are GitHub Actions self-hosted runners worth it?
For private repos with high volume (more than 5000 minutes/month), yes. You save paid minutes in exchange for operating machines. For public repos, no — security risk (malicious forks running code on your machine) outweighs benefit. ARC (Actions Runner Controller) helps at scale, but adds a Kubernetes layer; only makes sense for teams already operating K8s.
Is Woodpecker stable enough in 2026?
Yes. Monthly releases, solid codebase (forked from Drone, which had five years of production), active community. In production at hundreds of small and medium-sized companies. It's not the safe bet "nobody is fired for choosing it" — that's Actions or GitLab — but in three years of fork there hasn't been a serious community incident. For a small self-hosted team, it's the sensible choice.
Do ArgoCD and FluxCD enter this decision?
Not directly. ArgoCD/FluxCD are GitOps tools for Kubernetes, not CI. They watch a Git repo and apply changes to the cluster. CI continues to be Actions/GitLab/Drone generating images; ArgoCD/Flux apply the deploy. If you're not on Kubernetes, ArgoCD/Flux aren't for you. Teams on other orchestrators deploy directly from CI or via the orchestrator's APIs.
How many simultaneous runners for a team of 5 devs?
Practical rule: one runner per two active developers, plus one extra runner so long builds don't block fast PRs. Five-dev team: three runners is comfortable. At peak times (release day), bring it up to five temporarily. Each runner consumes 1 to 2 GB of RAM in typical workload; an 8-GB server runs four runners without pain.
Dependency cache — which tool handles it best?
GitLab CI has native cache by key/path, integrated with the own registry. GitHub Actions has actions/cache (free, 10 GB per repo). Drone/Woodpecker depend on external cache plugin (S3, local MinIO) — more setup but more flexible. At moderate volume, all solve it; at high volume (large monorepo), GitLab has an advantage from registry integration.
Migrating from GitHub Actions to Drone — how much work?
For simple workflows (build + test + push), 1 to 2 days. For workflows that depend on many marketplace actions, 1 to 2 weeks (need to rewrite each action as container). The biggest pain is secrets and environments — export and reimport carefully. Recommendation: migrate project by project, not all at once.
Can I run Actions and Drone/Woodpecker runners on the same server?
Technically yes, both are containers. In practice, isolation improves: runners on separate servers avoid one heavy build affecting the other. If the budget is tight, two R$40/month servers are better than one R$80/month server with everything together.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
In summary
CI/CD in 2026 has no winning tool. It has usage profiles and honest tradeoffs:
- You're on GitHub and the volume is light to medium? Actions, free plan. Don't look for problem where there isn't one.
- You're on self-hosted GitLab? Native GitLab CI. Already paid.
- You want predictable cost and have 1-2h/month of ops available? Woodpecker self-hosted on a R$80 VPS. The most economical choice.
- You have a large monorepo with complex workflow? GitLab CI (native DAG) or Actions with reusable workflows.
- You have high volume and minute pricing pain? Hybrid: Actions for light workflows, self-hosted runners for heavy ones.
If you're thinking of running the CI tool as part of the same cluster that serves production — with real high availability, integrated metrics, and backup without setting up a separate stack — install HeroCtl on a server:
curl -sSL https://get.heroctl.com/install.sh | sh
From there, describing a Woodpecker server with three auto-scalable runners is a fifty-line configuration file. The cluster takes care of the rest: distributes the runners across the nodes, keeps the server available even with machine loss, backs up state, exposes metrics in the embedded panel.
For more context, also worth reading Docker deploy in production: from compose to a cluster — discusses when it makes sense to leave docker-compose for a replicated control plane, with the same honest criteria as this post. And for teams thinking of simplifying the entire orchestration stack, Migrating from Kubernetes to a simpler stack — real case has numbers from a real migration, with gains and pains.
The CI/CD choice is one of the most enduring decisions of the team. Worth a few days of honest comparison before copying the .github/workflows/ from the previous project — because three years later, migration costs dearly.