Skip to main content

Lagoon Outlived Its Mirantis Acquisition: What a Drupal-Born Kubernetes PaaS Still Gets Right

9 min readDora NodaDora Noda
Share
On this page

Amazee.io, a Swiss Drupal shop, built Lagoon in 2017 to stop hand-rolling Kubernetes YAML for every client site. Mirantis bought the company in 2022. Four years and one acquisition later, Lagoon is still cutting releases — v2.26 shipped in early 2026 — while a wave of newer, general-purpose "self-hosted Heroku" projects (Coolify, Dokploy, Kubero, Canine) fight over the same territory Lagoon staked out years earlier. The reason it's still shipping isn't luck: every branch deploy on Lagoon runs drush config-import, provisions a database, and clears caches automatically, without a human writing a deploy script — a level of CMS-specific automation none of the newer general-purpose entrants have matched. This is a look at exactly how that automation works, what a framework-agnostic git-push platform has to build to reach the same bar generically, and what surviving a corporate acquisition intact says about betting on "Kubernetes underneath, opinionated git-push on top" as a durable architecture.

Lagoon Isn't Drupal-Only — It's Just Shaped by Drupal

Worth stating plainly, because it's easy to overclaim: Lagoon isn't a Drupal-specific tool the way, say, a managed WordPress host is WordPress-specific. It runs any Docker image, and its own docs list PHP, Node.js, Go, Python, Laravel, Symfony, and WordPress as supported stacks alongside Drupal.

What's Drupal-shaped isn't the runtime — it's the defaults. The build hooks, the database-provisioning behavior, and the multi-site pattern all assume a Drupal (or Backdrop) site's actual deploy problem, because that's the problem amazee.io's own client work was solving when Lagoon was built. A Go API deployed on Lagoon gets none of that automation for free; a Drupal site gets almost all of it out of the box.

That distinction matters for reading the rest of this piece correctly: what follows isn't "Lagoon can only run Drupal," it's "here's what Lagoon does for Drupal that a general-purpose platform's generic container runtime doesn't do for anything."

The Architecture: Core and Remote, Not a Cluster-API Control Plane

Lagoon splits into two halves. Core runs the API, authentication, and the web UI — the parts a developer or CI pipeline talks to. Remote is a set of controllers (the remote-controller project) that runs inside each target Kubernetes cluster and handles the actual build, deploy, and teardown of workloads on that cluster. A Lagoon installation can point Core at multiple Remotes running on EKS, AKS, GKE, or a self-managed cluster simultaneously.

What Lagoon does not do is provision the cluster itself. There's no Cluster API integration, no Machine resource, no node-lifecycle reconciliation loop — Lagoon assumes a working Kubernetes cluster already exists and installs its Remote controller into it via Helm. That's a materially different scope than a platform whose control plane owns the fleet end to end: a Cluster-API-based platform reconciles bare-metal or cloud nodes into a cluster and then schedules workloads onto it as one continuous loop, so "the platform is down" and "the machines are gone" are the same failure mode to reason about.

Lagoon's Core/Remote split means the platform layer and the cluster-provisioning layer are two separate concerns, maintained by two separate teams in most real deployments. amazee.io's Lagoon-as-a-Service (LaaS) offering, for instance, explicitly expects a customer's own ops team to have already stood up the EKS cluster Lagoon's Remote agent gets installed into.

Neither model is strictly better; they're solving different problems. Lagoon's split is the right shape for a platform team that already runs Kubernetes for other things and wants a deploy layer on top of it. A Cluster-API-based platform's single reconciliation loop is the right shape for a team that wants to go from "own some machines" to "running fleet" without operating a second piece of infrastructure just to get a cluster to point Lagoon's Remote at in the first place.

What Actually Happens on a Drupal Branch Deploy

This is the part worth being concrete about, because "CMS-specific defaults" is vague until you see the mechanism. A Lagoon project's .lagoon.yml defines build-time and post-rollout tasks — Drush commands that run automatically, on every environment, every time a branch deploys:

yaml
tasks:
  build:
    - run:
        name: drush deploy prep
        command: drush -y sql-query "SET GLOBAL innodb_strict_mode=0" || true
  post-rollout:
    - run:
        name: drush config-import
        command: drush -y config-import
    - run:
        name: drush database updates
        command: drush -y updatedb
    - run:
        name: cache clear
        command: drush -y cache-rebuild

That's the config-import step this piece opened with: push a branch, and Lagoon runs the migration/config-sync sequence a Drupal deploy actually needs, without a developer writing that sequence into a CI job by hand. Three more defaults compound on top of it:

  • Per-branch environment variables. Lagoon reads .lagoon.env.BRANCHNAME files (with special characters sanitized via $LAGOON_GIT_SAFE_BRANCH), so each branch's deploy can carry its own config without touching the shared .lagoon.yml.
  • The DBaaS operator. Set lagoon.type: mariadb for a service and Lagoon's DBaaS operator provisions a managed database for that environment if the cluster has one configured, falling back to an in-cluster MariaDB pod if not — either way, a database exists for that branch without a human running a provisioning script.
  • Polysite. The same Git repository can back multiple Lagoon projects, each with its own isolated database and persistent-files volume — the pattern a Drupal agency running dozens of near-identical client sites off one shared codebase actually needs.

None of this is exotic engineering. It's exactly the automation a Drupal deploy has needed for over a decade, wired directly into the platform instead of left to whatever CI script a given agency happened to write. That's the whole value proposition in one sentence: Lagoon didn't invent a new deploy primitive, it just stopped making every Drupal shop reinvent the same one.

What a General-Purpose Platform Has to Build Instead

A framework-agnostic git-push platform can't hard-code drush config-import — the next tenant might be running Rails migrations, a Django management command, or nothing at all. So each Lagoon default has to become a generic primitive instead of a Drupal-specific one:

Lagoon default (Drupal-specific)Generic primitive a general-purpose platform needs
post-rollout Drush hooksA pre/post-deploy hook stage any framework's own migration command can bind to
DBaaS operator, per-branch MariaDBA per-branch/per-PR database isolation primitive, framework-agnostic
.lagoon.env.BRANCHNAMEPer-branch/per-environment secret and env-var scoping as a first-class object
PolysiteA one-repo-to-many-isolated-instances pattern

Here's the part that makes "still has to build" a checkable claim rather than an assertion: none of the general-purpose self-hosted PaaS projects this blog already covers have shipped the full combination yet.

  • Coolify ships automatic PR preview deployments with per-environment (production/preview) scoped env vars, but per-branch database isolation is a documented workaround — a shared preview Postgres instance, not automatic per-branch provisioning — and there's no built-in pre/post-deploy hook stage in the docs.
  • Dokploy generates a unique preview URL per PR and lets you construct a per-preview DATABASE_URL by hand using the PR number, but automatic database provisioning and lifecycle hooks on preview create/delete are still open feature requests in its GitHub issue tracker, not shipped behavior.
  • Kubero comes closest: its review apps auto-build and auto-clean-up on PR open/close, and it can deploy Postgres, Redis, MySQL, or MongoDB add-ons alongside an app in one step — but nothing in its docs confirms that add-on deployment is automatically isolated per review app the way Lagoon's DBaaS operator is scoped per branch by default.
  • Canine offers preview environments and Helm-based database deployment, with no documented hook stage or automatic per-branch isolation either.

So the honest read isn't "Lagoon solved something trivial that everyone else already has." It's "four newer, general-purpose platforms have all shipped the easy 80% — branch-triggered preview URLs — and none has shipped the specific combination of automatic per-branch data isolation plus a built-in deploy-hook stage that made Lagoon's Drupal automation actually save a developer's time, not just their build command."

Surviving an Acquisition Is Its Own Signal

This blog has already covered what happens when a platform's roadmap depends on someone else's cap table: Heroku moved to sustaining-engineering-only status in 2026, and Gitpod pivoted away from managed cloud dev environments entirely after rebranding to Ona. Both are cautionary tales about what a funding or ownership event can do to a platform a team is depending on.

Lagoon is the counter-example, not because Mirantis is a uniquely benevolent owner, but because of what didn't change: it's still Apache-2.0 licensed, still developed in the open on GitHub under the uselagoon org, and still shipping — v2.26 landed in early 2026, four years after the acquisition closed, with the same Core/Remote architecture and the same Drupal-shaped defaults it had before Mirantis bought the company. A vertically-focused, open-source project with a real production user base (Drupal agencies who'd have to rebuild their entire deploy pipeline to leave) gives an acquirer less room to fold the roadmap into something else than a venture-funded SaaS product with a thinner moat does. Staying open source is what actually made that durable — a proprietary acquisition target has no equivalent forcing function keeping the acquirer honest.

The Takeaway Isn't "Copy Lagoon's Drupal Hooks"

It's that the primitives underneath those hooks are the reusable part. A general-purpose git-push platform doesn't need to know what drush config-import is — it needs a deploy-hook stage generic enough that a Drupal shop could wire that exact command into it themselves, a per-branch database isolation primitive that doesn't assume MariaDB, and per-branch secret scoping that isn't bolted on as a workaround. Build those three generically, and a Drupal agency self-hosting on owned infrastructure could reconstruct Lagoon's automation without the platform ever having to special-case a CMS.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with deploy hooks and per-branch environments as first-class primitives instead of a framework-specific special case. Star the repo on GitHub or deploy your first app today.

Sources:

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex