Podman #4 - Networking - Layer 2 Dragons

September 22, 2025

Finally, networking. I’ve been doing it for over 20 years, so let’s set some boundaries and enforce a few paradigms.

podman network create test

Yes, this creates a network just like in Docker. But I don’t want my containers talking to each other, and I definitely don’t want them talking to the internet.

The documentation I used to figure this out is here (again), but there are two key concepts that stood out: internal and isolate. And they can be combined!

Internal

Internal=true

This restricts external access for the network. In other words, containers won’t have a default gateway and can’t connect to the internet. This even improves startup time for some containers, like OpenSearch.

Isolate

Options=isolate=true

This option blocks traffic between networks that have it enabled. Networks with isolate=true can’t talk to each other, but they can talk to networks without isolation.

Strict Isolation

It turns out:

--opt=isolate=strict

…is also a thing. I stumbled across it on Reddit or GitHub (not well documented). With strict, the network can’t talk to anything unless that other network also has isolation enabled. Unlike true, it won’t communicate with “normal” networks.

Perfect. This means if we combine internal and isolate, we can build a network where nothing leaks out. That will be the default setup from here on.

Examples

podman network create --internal internalNet
podman network create --opt=isolate=true isoTrueNet
podman network create --opt=isolate=strict isoStrictNet
podman network create --internal --opt=isolate=true intIsoTrueNet
podman network create --internal --opt=isolate=strict intIsoStrictNet

Now let’s run some containers and see if they can ping each other:

podman run --rm -it --network=isoStrictNet alpine sh
podman run --rm -it --network=intIsoTrueNet alpine sh

And yes — it behaves exactly as described.

Rootless Quadlet

Since we’re not using the CLI to deploy services, we need to define this network in a rootless Quadlet file:

nano ~/.config/containers/systemd/intisostrictnet.network

[Unit]
Description=Isolated internal network

[Network]
Driver=bridge
Internal=true
Options=isolate=strict

[Install]
WantedBy=default.target

Then generate the systemd file:

systemctl --user daemon-reload

You can check if it was created with:

ls /run/user/$UID/systemd/generator/

If it isn’t there, troubleshoot with:

/usr/lib/systemd/user-generators/podman-user-generator -dryrun

If all looks good, start the service:

systemctl --user start intisostrictnet-network.service

Now podman network ls will show your shiny new network.

We can put a container on it to verify that we have no network connectivity:

 podman run --rm -it --network=systemd-intisostrictnet alpine sh

That’s it for Podman #4 – Networking.

https://docs.podman.io/en/latest/markdown/podman-network-create.1.html


Podman #3 - Rootful vs Rootless Cheat Sheet

September 21, 2025

So far we’ve looked at running containers with Podman as root and as a regular user. The concepts are the same, but the commands and file locations differ. Here’s a quick cheat sheet to keep things straight.

Quadlet File Location

  • Rootful: /etc/containers/systemd/
  • Rootless: ~/.config/containers/systemd/

systemctl Reload

  • Rootful: sudo systemctl daemon-reload
  • Rootless: systemctl --user daemon-reload

Service File Location

  • Rootful: /etc/containers/systemd/<name>.service
  • Rootless: /run/user/$UID/systemd/generator/<name>.service

Start the Container

  • Rootful: sudo systemctl start alpine.service
  • Rootless: systemctl --user start alpine.service

Enable at Boot

  • Rootful: sudo systemctl enable alpine.service
  • Rootless: systemctl --user enable alpine.service
    (also requires sudo loginctl enable-linger $UID)

Targets

  • Rootful: multi-user.target
  • Rootless: default.target

Generators

  • Rootful: /usr/lib/systemd/system-generators/podman-quadlet -dryrun
  • Rootless: /usr/lib/systemd/user-generators/podman-user-generator -dryrun

Summary

Rootful Podman behaves almost exactly like Docker in terms of how systemd manages services. Rootless Podman adds an extra layer of isolation (and complexity), but with the right commands and paths, it works just as smoothly. This cheat sheet should help you switch between modes without second guessing.


Podman #1 - Everyone is talking about podman

September 20, 2025

I’ve been using Docker forever, and everything was fine until the licensing changed and suddenly you couldn’t use it at work anymore. So, it’s time to pivot to Podman. Great. Everyone’s talking about it, so you figure, why not give it a try? But then you realize development is moving so fast that most of the documentation is already outdated before you even get started. Now, with the release of Debian Trixie, it’s finally time to sort this out.

In this series, I want to figure out how to use Podman as a replacement for Docker. For example, starting containers at boot using Quadlets. I work with Kubernetes quite a bit, so learning how pods work in Podman is a nice bonus. Network control and policies are also important, so we’ll take a look at those too.

Using Debian Trixie, we get a fairly recent version of Podman to work with, which makes experimenting a lot easier.

sudo apt install podman

podman --version
podman version 5.4.2

Step 1: Create a Quadlet

Let’s begin with a simple container. We’ll create a Quadlet in the default location for a root container:

nano /etc/containers/systemd/alpine.container
[Unit]
Description=Alpine test container

[Container]
Image=docker.io/library/alpine:latest
Exec=sleep infinity

[Service]
Restart=always

[Install]
WantedBy=multi-user.target

Save and close the file. At this point, nothing happens yet — no container is running.

Step 2: Reload systemd

Podman relies on systemd to manage services behind the scenes. To get systemd to notice our new Quadlet, reload it:

systemctl daemon-reload

This triggers "/usr/lib/systemd/system-generators/podman-quadlet", which generates the actual service file in "/etc/containers/systemd/".

Step 3: Troubleshoot if needed

If you run "systemctl daemon-reload" and no .service file appears in "/etc/containers/systemd/", try this command:

/usr/lib/systemd/system-generators/podman-quadlet -dryrun

It will tell you what went wrong in the Quadlet file ("/etc/containers/systemd/alpine.container" in our case).

Step 4: Start the container

If everything worked, you should see a file called alpine.service:

ls /etc/containers/systemd/

Now you can start it just like any other systemd service:

systemctl start alpine.service

And just like that, the container is up and running.

Step 5: Startup on boot

You might notice that the container already comes back after a reboot, even without manually enabling the service. That’s because Podman includes a helper service:

podman-restart.service

On boot, this service regenerates the systemd unit files in the documented locations and automatically starts them if the policy is set in your Quadlet. In other words, as long as you’ve configured "Restart=" or similar policies, Podman takes care of the boot startup for you.

https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html