Hi all, I want to show off a project I’ve been building: Bundar.
What it is: a push-to-deploy platform. You bring your own server (any VPS), connect it, and then:
git push bundar main
→ container image built
→ health checks
→ live, zero downtime
Push-to-deploy, health-checked deploys, automatic HTTPS. You can try it for free — point it at a VPS and push.
Why it’s an Elixir story. The whole control plane is a Phoenix app, and Elixir is doing the heavy lifting:
- Phoenix LiveView — the entire dashboard. Deploy logs, server status, service config, all live.
- Postgres — the source of truth for servers, services, and deploy state.
- Oban (free version) — the deploy and provisioning pipeline. One deploy fans out into Oban jobs: build the image, roll it out, run health checks, clean up. Server onboarding is jobs too (install Podman, configure firewalld, set up the reverse proxy, configure sshguard), plus a scheduled ping job that watches every server’s health.
- PUI — our own LiveView UI toolkit, extracted from building this dashboard and open-sourced on Hex (MIT): pui | Hex . The whole UI is built on it.
- NimblePublisher — powers the blog (bundar.dev/blog)
A couple of implementation details that might interest folks here:
- SSH client, forked from librarian. The management plane is SSH hub-and-spoke — the control plane talks to customer servers over Erlang’s
:ssh. The client started as librarian, which we forked and later ported in-app (SSHClient) so we could evolve it alongside Bundar: streaming command output as anEnumerable, connection/identity handling, and SSH tunneling, which we use when setting up the reverse proxy on a fresh server. - Remote curl as a Req adapter. Some HTTP calls need to happen on the server itself (registries, local endpoints), not from the control plane. So we wrote a custom
Reqadapter: the app builds a request with the normal Req API, and the adapter translates it into acurlcommand, executes it remotely over SSH, then parses the rawcurl -ioutput back into a%Req.Response{}. The rest of the codebase never knows the request physically ran somewhere else. - Containers run under Podman with
--userns=auto, so container root maps to an unprivileged host UID. We’re writing about the Podman isolation model on our blog: https://bundar.dev/blog - Caddy for reverse proxy / load balancer. Each server runs a Caddy container (
bundar_proxy) provisioned by an Oban job, and routes are configured dynamically through Caddy’s admin API — which is exactly what the remote-curl Req adapter talks to, over the SSH tunnel.
A note on how it was built. I started learning Elixir & Phoenix in April 2023, and this side project began around October 2023 — before agentic coding was a thing. I bought the bundar.run domain in February 2024 (it expired in February 2026, so now we’re on bundar.dev). These days AI agents help me move a lot faster, but I still review and test everything manually.
One question for the forum. No plans to open-source Bundar for now, but self-hosting is a real possibility down the road. I’d love advice on the best way to ship a compiled release with feature flags — e.g. a self-hosted build with the billing, blog, and landing page modules stripped out. Application env + compile-time config? Runtime feature flags? Something else entirely? Curious how others have done this with mix release.
I’d love feedback — especially from anyone running Oban pipelines in production, or building LiveView dashboards. What would you want to see next? Happy to answer questions about the stack.
Thanks for reading!



















