Installing Podman Locally and on a VPS

Category: On-Premises & Private Cloud

Tags: podman


I wanted the same container runtime on my MacBook and on the VPS — same commands, same compose file, same mental model. I picked Podman over Docker mostly because it runs rootless by default and doesn’t need a daemon on Linux, which matters on a small VPS where every background process costs something.

This post is the install baseline the rest of the series rests on.

The setup

  • Local: macOS with Homebrew, Podman running inside its managed VM.
  • Server: Debian/Ubuntu VPS, Podman installed from the distro packages.
  • Compose: podman-compose on both sides — no Docker binaries involved.

Local installation (macOS)

brew update
brew install podman podman-compose

Podman on macOS runs inside a lightweight Linux VM — initialise and start it:

podman machine init
podman machine start

A quick sanity pass to make sure the client can reach the VM:

podman --version
podman info
podman system connection list

Gotcha: Homebrew installs both podman compose (the subcommand) and podman-compose (a separate Python project). The subcommand will happily call out to docker-compose if it’s on the PATH, which defeats the point. Pin the provider explicitly:

export PODMAN_COMPOSE_PROVIDER=/opt/homebrew/bin/podman-compose
echo 'export PODMAN_COMPOSE_PROVIDER=/opt/homebrew/bin/podman-compose' >> ~/.zshrc
source ~/.zshrc

Server installation (Debian/Ubuntu VPS)

sudo apt-get update
sudo apt-get -y install podman podman-compose git

Same verification pass as on macOS:

podman --version
podman info
podman system connection list

Problem: On multi-container compose setups I wanted service-name DNS (one service reaching another by its compose service name) to work out of the box. The default installation doesn’t always include the network plugins that provide this.

Implementation: Install the netavark + aardvark stack — Podman’s modern network + DNS layer:

sudo apt-get -y install netavark aardvark-dns

Solution: Compose services resolve each other by name inside the Podman network, without me having to hand-wire IPs.

First-run check (both environments)

Pulling a real image and running a throwaway container is the cheapest way to confirm the runtime and the registry path both work:

podman pull docker.io/library/node:20-bookworm-slim
podman run --rm docker.io/library/node:20-bookworm-slim node --version

A Node version back on stdout means the whole chain — runtime, image resolver, registry auth, container exec — is healthy.

Common first-install issues

  • connection refused on the Podman socket (macOS). The VM is either not running or in a bad state. podman machine stop && podman machine start resolves it almost every time.
  • invalid username/password pulling public images. Stale auth state in ~/.config/containers/auth.json. A plain podman pull often clears it without any extra login.
  • podman compose picks the wrong provider. Set PODMAN_COMPOSE_PROVIDER explicitly — don’t rely on the auto-detection.

Day-to-day commands

The three commands I run ninety percent of the time:

podman compose -f compose.yml up --build -d
podman compose -f compose.yml logs -f
podman compose -f compose.yml down

Same three commands on laptop and server — that’s the whole point.

What to take away

  • Keeping the local and server installations symmetric pays off on every deploy — no mental translation needed.
  • Rootless Podman is the default, and it’s the right default for a small single-service VPS.
  • Pin PODMAN_COMPOSE_PROVIDER explicitly; the auto-detected fallback to docker-compose is a silent behaviour change waiting to happen.
  • Install netavark and aardvark-dns on the server if you use compose service-name DNS.
  • A successful podman run --rm node:20 node --version is a complete smoke test.