Skip to main content

Networks & Subnets

Every workspace's containers talk to each other over Docker networks. Miabi manages these for you — you rarely need to think about them — and allocates their IP subnets from its own pool so a busy host never runs out of address space.

Workspace networks​

Each workspace gets a default network the moment it is created. It is fully platform-managed:

  • Every application, database, and job in the workspace is automatically attached to it, so they can reach each other by name. Each container answers to its resource's own name, which is what {{ .applications.<name>.host }} resolves to in a manifest.
  • You cannot detach a resource from the default network, and you cannot delete it — it is the shared backbone the workspace relies on.

You can also create additional networks to further segment traffic (for example, isolating a group of apps). Attach apps to them alongside the always-present default. Custom networks count against your plan's network quota; the default network does not.

Bridge on one node, overlay across a cluster​

The default network's driver depends on whether cluster mode is on, and this is what decides whether a workspace can span hosts at all:

DriverReach
Single node / no clusterbridgeNode-local. Recreated on each node with the same name and subnet, but as disconnected islands — an app on one node cannot reach a database on another, and Miabi refuses to attach them.
Cluster modeoverlaySpans every node, attachable and encrypted. An app on one node reaches a database on another by name, with no published ports.

Because every app and database already joins the workspace default, that one change is the whole mechanism for cross-node connectivity — nothing else has to be reconfigured, and no connection string changes (they address a database by its alias, not by its network).

Enabling cluster mode converts existing workspaces for you. Containers are not restarted: each is attached to the overlay, carrying its DNS aliases across, and then detached from the bridge. See Applying it to existing workspaces.

note

The reverse-proxy (gateway) network is separate: only apps exposed by a route join it, keeping unexposed apps off the public path. See Reverse proxy.

The shared gateway network​

The gateway and every routed app/database container share one bridge network — miabi-proxy by default (MIABI_PROXY_NETWORK). Because all exposed containers on the host pile onto this single network, it needs plenty of address space, so it is treated differently from the per-workspace networks above:

  • A roomy, explicit CIDR on a managed install. install.sh and miabi setup create it with 10.63.0.0/16 (~65k addresses) instead of a slice of Docker's small default pool. Choose another range with MIABI_SUBNET or --subnet — see Changing the subnets.
  • Distinct from the workspace pool. Its CIDR must not overlap MIABI_NETWORK_POOL_CIDR (the 10.64.0.0/12 pool below) or your LAN. The default 10.63.0.0/16 sits just outside the pool.

The Compose files declare miabi-proxy as an ordinary Compose network, so Compose creates it (and docker compose down removes it) with a subnet from Docker's default address pools. Add an ipam block to the network in your Compose file if you need a specific range.

The platform's private network​

Miabi's own components do not sit on the shared gateway network. They get a second, private bridge — miabi-internal — and only the gateway is on both. This is true of every install path: a managed one (install.sh, miabi setup) creates it with an explicit 10.62.0.0/16, and the Compose files declare it alongside miabi-proxy.

Containermiabi-proxy (shared)miabi-internal (private)
miabi-gateway✅ reaches your apps, and the internet for ACME✅ reaches Miabi and Redis
miabi (control plane)—✅
miabi-postgres—✅
miabi-redis—✅
mb-registry (built-in registry)—✅
your routed apps✅—

(On the Traefik Compose variant, Traefik takes the gateway's row.)

The reason is that everything on the shared network can dial everything else on it by name. The control-plane database has a single superuser password covering every workspace and every stored secret, and Redis holds the background job queue — so one exposed application being compromised should not put them within reach. After the split, the only way into the platform is through the gateway, where your routes and middlewares — rate limits, IP allowlists, security policies — actually apply.

The built-in registry sits on the private network only for the same reason: it relies on the gateway to authenticate every request, so app containers must not be able to reach it directly. A Compose stack needs MIABI_INTERNAL_NETWORK set for this — see the upgrade note below.

Nothing about deploying, routing, or connecting apps changes: your containers still join miabi-proxy when they have a route, exactly as before.

Changing the subnets​

On a managed install both CIDRs must miss each other, the workspace pool (10.64.0.0/12, below), and your LAN or VPN:

curl -fsSL https://get.miabi.io | sudo \
MIABI_DOMAIN=miabi.example.com \
MIABI_SUBNET=10.63.0.0/16 \
MIABI_INTERNAL_SUBNET=10.62.0.0/16 bash

They are also flags on miabi setup (--subnet, --internal-subnet) and are recorded in /etc/miabi/miabi.yaml:

spec:
networking:
proxy: { name: miabi-proxy, subnet: 10.63.0.0/16 }
internal: { name: miabi-internal, subnet: 10.62.0.0/16 }

The same block also carries the managed subnet pool (pool), the host port range (hostPorts), the default cluster's wildcard domain for one-click app URLs (external — other clusters set theirs in the cluster's Edit dialog, see Cluster mode) and the managed-DNS interval (dns) — see the Install Manifest.

Upgrading an existing install

The split happens on your next miabi upgrade. The private network is created, every component is attached to it while still running, and only then are the containers recreated on their final networks — so there is no window where Miabi cannot reach its database. Expect the same brief restart any upgrade involves.

Upgrading a Compose stack

Pull the new compose.yaml, add MIABI_INTERNAL_NETWORK=miabi-internal to your .env, and docker compose up -d — Compose creates the network and moves the containers onto it. The variable matters: Miabi reads it to place the helper containers it runs out of process (platform backups, the built-in registry), and an empty value leaves them on the proxy network alone, where the database no longer is.

On the Traefik variant the control plane also carries traefik.docker.network=miabi-internal, since Traefik's Docker provider otherwise looks for the panel's address on the proxy network and finds none.

Installs created before miabi-proxy

The shared network used to be called miabi. An existing install keeps that name: miabi setup recorded it in the manifest, and a control plane with MIABI_PROXY_NETWORK unset finds the network on the engine instead of creating a new one. On a Compose stack, keep MIABI_PROXY_NETWORK=miabi in your .env — the compose file names the network from it, and an unset value creates miabi-proxy and moves the gateway away from your apps. Compose also refuses the old network, because it was created under the old key, so adopt it with a compose.override.yaml next to compose.yaml:

networks:
miabi-proxy:
name: miabi
external: true

Managed subnet allocation​

Docker's built-in address pools are small and shared across every network on a host. A platform that creates many networks — one default per workspace, plus stack, custom, and cluster overlay networks — can exhaust them, and Docker then fails network creation with:

all predefined address pools have been fully subnetted

To avoid this, Miabi manages the IP space itself: it carves a unique subnet out of a configurable network pool for every network it creates and hands Docker an explicit subnet, instead of relying on Docker's defaults.

SettingDefaultControls
MIABI_NETWORK_POOL_CIDR10.64.0.0/12The private supernet subnets are carved from.
MIABI_NETWORK_SUBNET_PREFIX24The size of each per-network subnet.

The default /12 pool split into /24s yields 4096 networks, each with 254 usable addresses. The base 10.64.0.0/12 is chosen to steer clear of ranges you're likely to already be using — common Kubernetes CNI ranges, Docker's own 172.16.0.0/12 and 192.168.0.0/16 pools, and typical LAN/VPN/Tailscale ranges. Shrink the pool (e.g. a /16, 256 networks) or move the base if it doesn't suit your environment.

How it behaves:

  • Unique & durable — allocations are recorded so they survive restarts and never overlap.
  • Overlap-safe — at start-up Miabi reserves the subnets of any pre-existing Docker networks so it never hands out a colliding one; if a chosen subnet still collides it is skipped automatically.
  • Reclaimed — deleting a network returns its subnet to the pool.
  • Consistent across nodes — a workspace network keeps the same subnet on every node it is recreated on.
tip

Pick a pool CIDR that doesn't overlap your LAN, VPN, or other private ranges on the host. The default 10.64.0.0/12 is chosen to avoid the usual suspects (common Kubernetes CNI ranges, Docker's own pools, and Tailscale's 100.64.0.0/10), but if it collides with something in your environment, set MIABI_NETWORK_POOL_CIDR to a free range — for example a spare /16 like 10.99.0.0/16.

IPv6​

Every Miabi-created network is IPv4-only by default, and says so explicitly — a network that inherits the daemon's own default follows a setting nobody chose here, which is how a host with IPv6 defaults enabled can end up giving containers routable addresses nobody reasoned about.

Turn it on with MIABI_NETWORK_IPV6=true, or in the install manifest:

spec:
networking:
proxy: { name: miabi, subnet: 10.63.0.0/16, ipv6: true }
internal: { name: miabi-internal, subnet: 10.62.0.0/16 }
pool:
cidr: 10.64.0.0/12
# ipv6UlaPrefix: fd42:6d69:6162::/48

The manifest's ipv6 drives both halves: the platform's miabi-proxy network and the workspace networks the server creates, so you cannot end up with a dual-stack proxy network in front of single-stack app networks.

Seeing what a network actually got​

The stored record says what Miabi asked for; the engine decides what exists. Two places show the engine's answer:

  • A workspace's Networks page — the info action on a row opens the network's details: driver and scope, the IPv4 subnet and gateway, whether IPv6 is on, and the IPv6 subnet and gateway. The same action sits on an application's Networks table.
  • Admin → Platform Settings → Networking — whether IPv6 is on for the install, how each /64 is chosen, the workspace subnet pool and the shared proxy network. It distinguishes requested from active: with MIABI_NETWORK_IPV6=true on an engine that cannot honour it, the page says so rather than showing a switch that looks stuck.

A cluster overlay only exists on a node once one of its containers attaches there, so a network whose details read "not present on the control-plane node" is ordinary rather than broken.

Once a container is running, its Network tab lists the address it was actually given. The IPv6 column appears only when a container has one — which is also how you spot a container started before IPv6 was switched on: its network is dual-stack, and it is not.

Addresses are ephemeral

A container's address changes on every redeploy, in both families. Address an app by its hostname or alias, never by IP.

Who picks the /64​

Address chosen byNeedsAddresses are
Default (no ULA prefix)DockerEngine 26+assigned by the daemon
ipv6UlaPrefix setMiabi, derived from the IPv4 subnetEngine 25+stable and predictable

Deriving maps the IPv4 subnet onto a /64 under your prefix, so the two stacks line up: 10.64.5.0/24 under fd42:6d69:6162::/48 becomes fd42:6d69:6162:4005::/64. The same network gets the same range on every node and after every recreate, which is what makes a firewall rule possible to write. The mapping is unique across the whole default pool, so no two networks collide.

Engine 25

Docker only assigns an IPv6 range itself from Engine 26. On Engine 25, enabling IPv6 without a ULA prefix would make every network create fail — so Miabi refuses to enable it, logs the engine version and the two ways forward, and carries on in IPv4. Set ipv6UlaPrefix to use IPv6 on Engine 25.

Cluster overlays stay IPv4

This covers bridge networks — a single-node install, and each node's local networks. Swarm overlay networks are not dual-stack yet, so a cluster's cross-node traffic stays IPv4 whatever this setting says.

Containers get routable addresses

A ULA (fd00::/8) range is private, like 10.0.0.0/8. But enabling IPv6 changes what a container can reach and be reached on, and Miabi has no IPv6 firewall policy of its own yet (the node network policy is still on the roadmap). Treat it as you would opening a new address family on any host.

Monitoring the pool​

Pool utilization is visible on the admin dashboard (a Subnet pool panel showing used vs. total subnets, with a warning as it nears capacity) and via Prometheus:

MetricMeaning
miabi_network_subnet_pool_usedSubnets allocated or reserved.
miabi_network_subnet_pool_totalPool capacity.

If the pool approaches exhaustion, enlarge MIABI_NETWORK_POOL_CIDR. See Monitoring and Configuration.