Your app is deployed and healthy, but only inside your cluster. Now you want it at a real address, app.example.com, over HTTPS, in a browser. On raw Kubernetes that means routing, a certificate, and DNS, three separate things that each fail in their own way. Burrow turns it into one command to publish and one command to check, and when a link in the chain is not ready, it tells you which one. This guide walks the whole path.
What "publish" actually sets up
When you publish an app, Burrow puts three things in place: a route from a public hostname to your app, a certificate so that route is served over HTTPS, and the reachability check that tells you whether it all came together. You give it the hostname and the port your app listens on, and it handles the wiring.
Here is the one command:
burrow app publish web --host app.example.com --port 8080 --tls
That says: route app.example.com to the web app on container port 8080, and request an HTTPS certificate for the host. The --tls flag is what gets you the certificate, and Burrow keeps it renewed for you afterward, so you are not tracking expiry dates or running a renewal job.
Point your domain at the cluster
A route inside the cluster is only half the story. The wider internet needs to know that app.example.com should go to your cluster, and that is DNS. You point the hostname at your cluster's public address.
If you manage DNS by hand, you add that record at your registrar the usual way. If Burrow is configured with your DNS provider, it can write the record for you, since a public DNS change is one of the actions it can gate, this is the kind of thing that can hold for your sign off before it goes out. Either way, the goal is the same: the name resolves to your cluster.
Check the whole chain, link by link
This is where most "why is my site not loading?" frustration usually lives, and where Burrow saves you the most time. Instead of a bare up or down, ask it to walk the chain:
burrow app reachability web
It checks each link in order and tells you which one is not ready: is the route in place, is there a public address, has the certificate issued, does DNS resolve. So instead of staring at a browser error, you get something like "the certificate has not issued yet," which is a status you can wait on rather than a mystery you have to debug.
Certificates in particular take a short while to issue the first time, and DNS takes a little while to propagate. Neither is a problem, they are just steps that are not instant. Reachability tells you which step you are waiting on, so you know the difference between "not ready yet" and "actually broken."
Reading the chain, link by link
It helps to know what the links are, because then a report tells you not just where you are stuck but roughly what to do about it. There are four, and they fail in different ways.
The route is the connection between the public hostname and your app. If this is the problem, the publish did not fully take, or you named a port your app is not actually listening on. The address is the public entry point to your cluster. If there is no address, the cluster does not yet have a public way in, which on some setups means an external address is still being assigned. The certificate is the HTTPS credential for the host. This is the one that most often shows "not ready yet" right after publishing, and the fix is usually just to wait a moment while it issues. DNS is whether the name resolves to your cluster's address out on the internet. If this is the broken link, the record either is not set or has not propagated yet.
Because reachability names which of these four is the holdup, you never have to test all four by hand. You go straight to the one that is not ready.
Wait for it to go live
Rather than checking over and over, you can ask Burrow to poll until the app is actually reachable, or until it gives up:
burrow app reachability web --wait
That sits and watches the chain complete, then returns when the app is live. It is the natural thing to run right after publishing: kick it off, let it poll while the certificate issues and DNS settles, and when it comes back green, open https://app.example.com. That is your app, on your own domain, over HTTPS.
When your agent does this for you
If you operate through an agent, publishing and diagnosing reachability are things it can do and is genuinely good at. Ask it to serve your app at a domain and it runs the publish, and if the site is not loading, "why isn't my site reachable at example.com?" is a question it can answer by walking the same chain you would.
Because reachability names the broken link, the agent is not guessing. It reads "the certificate has not issued yet" and tells you to wait a moment, or it reads "DNS does not resolve to the cluster" and fixes the record, if you have let it write DNS. The link by link check is what turns a vague "it is down" into a specific, fixable problem, whether you or your agent is looking at it.
Note that exposing an app publicly and writing DNS are exactly the kind of actions Burrow can hold for your confirmation. So when an agent goes to put something on the public internet, that can come back to you for a sign off first. Making an app reachable to the whole world is a decision worth a human nod, and you decide, per environment, whether it needs one.
Getting the port right
The one detail people trip on is the port. The --port you pass to publish is the port your app actually listens on inside its container, not the public port. Someone visits over the standard web ports, 80 for HTTP and 443 for HTTPS, and Burrow maps those to your app's port for you. So if your app serves on 8080, you pass --port 8080, and visitors still just type https://app.example.com with no port in the URL.
If reachability tells you the route link is the problem right after a clean publish, a mismatched port is the usual culprit: you told Burrow to forward to a port your app is not listening on, so the connection has nowhere to land. Check what your app binds to, republish with the right port, and the link comes up.
The public entry point: a load balancer or a free path
Before any of this works, your cluster needs a public way in, and how that gets set up depends on where your cluster lives. This is the one place the setup differs by cluster type, so it is worth understanding once.
On a managed cloud like DigitalOcean, the public entry point is usually a load balancer, a real resource the provider bills for by the month. When you install the ingress stack, standing that up is a cost decision, so Burrow does not spring it on you: on the setups where it would create a billable resource, it holds for an explicit cost approval before provisioning. You approve it once, the load balancer comes up, and every app you publish afterward shares that same entry point at no extra per app cost.
On a single node cluster or a plain VPS, you do not need a paid load balancer at all. A lightweight path (a built in service load balancer, or MetalLB handing out the node's own address) gives you a public entry point for free. Same publish command, same reachability check, same result at your domain: only the plumbing underneath differs. If you are running Burrow on one box to keep costs down, this is the path you want, and it is the default on that shape of cluster.
The practical upshot: the first app you publish may involve a one time approval to create the entry point, and every app after it just rides on what is already there. Once the entry point exists, publishing is genuinely one command.
Letting your agent write DNS, on your terms
Pointing the domain at your cluster is a DNS write, and if you have connected a supported provider (DigitalOcean or Cloudflare), Burrow can make that record change for you instead of you clicking around a registrar. Because a public DNS change is consequential, it sits behind guardrails you control: dns.write for creating or updating a record and dns.delete for removing one.
That means you decide, per environment, how much an agent can do here. A common setup is to let DNS writes happen freely in staging and hold them for your confirmation in prod:
burrow guard set dns.write confirm --env prod
burrow guard set dns.write allow --env staging
With that in place, when an agent goes to publish a prod app and needs the DNS record written, the write pauses and comes back to you for a sign off. You approve, the record goes out, and reachability starts resolving. In staging the same flow just runs. The agent never quietly changes where your domain points in production without you saying yes.
Serving several apps at once
You are not limited to one published app. Each app gets its own hostname, and they can share the same cluster and the same public entry point without stepping on each other:
burrow app publish web --host app.example.com --port 8080 --tls
burrow app publish api --host api.example.com --port 3000 --tls
Two apps, two hostnames, two certificates, one cluster. Each one is routed to the right app on the right port, and each gets its own reachability check. This is how a small collection of services, a site, an API, an admin panel, all live together on infrastructure you own, each at its own clean address over HTTPS.
Taking it back down
If you need to stop serving an app at its hostname, there is a clean way to undo it:
burrow app unpublish web
That removes the public route and stops serving the app at that host. The app keeps running inside the cluster, it is just no longer exposed. Useful when you are retiring a domain, taking something private again, or moving an app to a different hostname.
The whole path, at a glance
burrow app publish web --host app.example.com --port 8080 --tls # route + certificate
# point app.example.com at the cluster (by hand, or via a DNS provider)
burrow app reachability web --wait # watch the chain go green
# open https://app.example.com
burrow app unpublish web # take it back down later
Getting an app onto the internet with a real certificate is one of those tasks that has a reputation for eating an afternoon, mostly because when it does not work, you cannot tell why. Burrow makes the setup a single command and, more importantly, makes the failure legible: it names the link that is not ready, so waiting feels like waiting instead of like being stuck.
What can go wrong, link by link
Publishing has a small number of failure shapes, and reachability points at each one. Here is what the common ones look like and what to do.
The certificate is stuck on "not issued yet" for a while. The most common reason a first certificate lags is that DNS is not resolving yet, because the certificate authority validates the host over the public name. If the DNS link is also not green, fix that first: the certificate often unblocks itself once the name resolves. If DNS is green and the certificate still has not issued after a few minutes, that is worth a closer look, but a short wait right after publishing is normal, not a fault.
The route link is red right after a clean publish. Nine times out of ten this is the port. The --port you pass is the port your app listens on inside its container, not 80 or 443. If you told Burrow to forward to a port nothing is listening on, the connection has nowhere to land. Check what your app binds to and republish with the right port.
DNS is green locally but not for everyone. DNS propagation is not instant and not uniform: your machine may see the new record before a visitor across the world does. This resolves on its own as the record spreads. Reachability checking green from the cluster's vantage is the signal that matters.
No public address at all. On a fresh cluster the external address can still be assigning, especially on a managed load balancer that is coming up for the first time. This is the entry point stage, not a per app problem, and it clears once the address lands.
A short FAQ
Do I have to publish over HTTPS? The --tls flag is what requests and maintains the certificate, and it is the path you almost always want: a real certificate, kept renewed for you, no expiry dates to track. Serving a real domain without it is rarely what anyone wants.
Can I move an app to a new hostname? Yes. Unpublish it from the old host with burrow app unpublish web, then publish it at the new one. The app keeps running inside the cluster the whole time; only the public route changes.
Does unpublishing delete my app? No. burrow app unpublish web removes the public route and stops serving the app at that host. The app keeps running inside the cluster, just no longer exposed to the internet. It is the clean way to take something private again.
Who pays for what? The shared entry point (the load balancer, where one is used) is the billable piece, and it is shared across every published app. Publishing additional apps onto an existing entry point does not add per app cost. On a single node or VPS setup, the free path avoids the load balancer bill entirely.
That legibility is the real gift here. The setup being one command is nice, but plenty of tools can hide the setup behind a button. What is rare is a tool that, when the site is not loading, tells you precisely which of the four links is the holdup instead of leaving you to poke at each one. That is the difference between a task that occasionally frustrates you for ten minutes and one that occasionally eats your whole afternoon. Publish, point DNS, watch it go green, and your app is live where the world can reach it.