Skip to main content

14 posts tagged with "docker"

View All Tags

Registry auth precedence: we never pull your images with our tokens

· 10 min read

Every time Fibe brings up a Playground, provisioning has to answer a small, unglamorous question: which credentials do we hand the Docker daemon so it can pull your private images? It's plumbing — but the wrong answer is the kind of thing that turns into a security incident with a postmortem and a regretful blog post.

So here is the one rule we refuse to bend: when we pull an image from GitHub Container Registry, we authenticate with your credentials and only your credentials. Fibe's own platform tokens never touch your pulls — not as a convenience fallback, not for a system mirror, not "just this once" to unblock a build. This post walks through how the credential merge works and what we get out of keeping our tokens on the other side of a wall.

Running AI coding agents as sidecars: the AgentChat deploy pipeline

· 21 min read

A user clicks "Start" on a Genie. Forty seconds later there's a Claude Code agent running in a container on their Marquee, reachable over TLS at a stable subdomain, talking to their repo through MCP, with their skills and main prompt already staged on disk. If the container later falls over, something notices within a minute and quietly puts it back.

That whole arc — from a config row in Postgres to a self-healing sidecar — is one job, one Redis lock, six steps, and a surprising amount of opinion about what counts as a failure. This post is a walk through the AgentChat deploy pipeline: what we build, in what order, which steps are allowed to fail and retry, and the one design decision that quietly shaped everything else — that a missed health check is not a reason to kill anything.

Routing with Traefik: wildcard TLS, maintenance pages, and a priority-one-million trick

· 10 min read

Every Fibe environment is a little pile of Docker containers on someone's machine, and the platform's job is to make that pile feel like a website: app.your-env.your-marquee.dev should resolve, terminate TLS, and land on the right container. When the environment is mid-rebuild with nothing healthy to land on, the URL should show a 503 that says "this is coming back," not a browser's "can't connect" screen.

That last part is where the interesting engineering lives. The happy path is solved; the hard question is the unhappy path: how do you serve a maintenance page from a router that has to beat the one that would otherwise serve the broken app, without playing whack-a-mole tearing routers down and back up? Our answer is a maintenance router with priority: 1_000_000 — blunt, exactly right, and the subject of this post.

Docker-truth reconciliation: making the database agree with `docker compose ps`

· 10 min read

There are two stories about what your Playground is doing. One lives in Postgres — a status that says running, error, or stopped. The other lives on a Docker host we don't own, reachable only over SSH, and you read it by running docker compose ps. Most of the time they agree; the interesting engineering is all about what happens when they don't.

This is a post about one reconciler and the small set of rules it uses to decide which story is true. The headline rule is easy to say and weirdly hard to honor: trust the daemon as the source of truth — but only when you can actually see it. A Playground on a host that clearly threw its containers away should be marked stopped; one on a host that simply didn't answer our SSH knock should be left exactly as it is. Telling those two apart is the whole job.

Stop is not delete: the data-volume preservation contract

· 10 min read

Here is the support ticket we never want to receive: "I stopped my Genie to save money over the weekend, and when I came back, everything it had learned about my codebase was gone."

For an AI coding agent, that's the worst possible failure. The agent's value compounds over time — its working state, its history, the files it wrote, the config it tuned — and all of it lives on disk, in Docker volumes, not in the container. So we drew a hard line early: stopping a Genie must never touch its data. Only one explicit, confirmed action deletes it. This post is about that contract and the small amount of code that makes "stop" and "delete" genuinely different.

The image build gate: one build per commit, verified

· 10 min read

Here is a bug that does not look like a bug. You push a commit, a build kicks off, it goes green, your environment comes up. Everything works. Except the image inside it was built from a different commit than the one you pushed — a stale checkout, a cached layer, a race between two builds for the same branch. Nothing errored. The dashboard says success. And you spend an afternoon debugging code that was never actually deployed.

That class of failure — a build confidently wrong about what it produced — is the one we decided never to ship to a Player. So Fibe's image builds follow one stubborn rule: a build must be deterministic and honest about which commit it produced. This post is about how each build record enforces that, and the two checks that will fail a build rather than let it lie.

Static or dynamic? Choosing image-only vs source-backed services

· 15 min read

Every service in a Fibe template is one of exactly two kinds, and the difference decides everything downstream: whether Fibe clones a repo, whether it runs a build, whether you can edit a file and watch the page reload, and whether a typo in a label hard-fails your launch. The kinds are static (runs a published image) and dynamic (source-backed — Fibe clones, builds, or mounts a Git repo). Most templates are a mix: a dynamic app service in front of a couple of static dependencies.

Here's the part people get wrong: the kind isn't something you declare. There is no fibe.gg/type label. Fibe derives it from the labels you set. Get the signals right and the classifier does the rest; get them subtly wrong and you'll see errors like Service 'web' has a build directive but lacks a fibe.gg/repo_url label. This guide walks the dividing line, then shows you how to wire each kind with real YAML you can copy.

Anatomy of a Playground: the eight-step creation pipeline

· 21 min read

You click "create," and a few seconds later there's a URL that serves your app. In between, a surprising amount happens — most of it on a machine you've never logged into. A Playground on Fibe isn't a container we start. It's a small, ordered ritual: claim a database row, clone source onto a remote host, merge registry credentials, build an image, render a compose file from a template, ship it over SSH, and only then run docker compose up — on the Marquee, not on us.

This post walks that ritual end to end. Eight steps, nine statuses, one set of legal transitions, and a fair amount of paranoia about what happens when a step fails halfway through. If you've ever wondered what "in progress" actually means while the spinner spins, this is the tour.

Subdomains, ports, and collisions: keeping environments uniquely addressable

· 9 min read

A Marquee is one Docker host. On it, a Player might run half a dozen Playgrounds at once — a couple of long-lived dev environments, a headless Trick chewing through a job, an AgentChat sidecar where a Genie is editing code. They share the same daemon, Traefik instance, range of host ports, and wildcard domain. And every one needs a URL that goes to exactly one place.

That's the whole problem: shared host, distinct addresses. Get it wrong and two environments fight over app.your-host or both grab host port 8080 — Traefik routes arbitrarily, or Docker refuses the second container, and the Player gets a confusing failure deep in the creation pipeline. Our answer is boring on purpose: we refuse to save an environment that would collide, before it touches the host. Validate at write time, routing stays dumb.

Tricks: headless one-shot Playgrounds for automation

· 9 min read

Most platforms ship a development environment and a CI system as two separate products. They drift. What worked in your dev container fails in CI for reasons nobody can reproduce, and you lose an afternoon discovering the CI runner is on a different base image, network, everything.

We didn't want two runtimes. A Trick is a Playground with one flag flipped — job mode — running the exact same environment engine, on the same Marquee, from the same Template. It has no UI to attach to and a defined notion of done: it runs, it exits, it records a result. If your tests pass in a Playground, they pass in a Trick — there's no second runtime to drift away from.