Skip to content

Instantly share code, notes, and snippets.

@kyletaylored
Last active January 2, 2026 23:41
Show Gist options
  • Select an option

  • Save kyletaylored/6d807ec1f6e0e6e0376432e8da24e075 to your computer and use it in GitHub Desktop.

Select an option

Save kyletaylored/6d807ec1f6e0e6e0376432e8da24e075 to your computer and use it in GitHub Desktop.
Guide for setting up Podman with Lima for better VM control.

Podman Desktop with Lima (Mutable VM)

Overview

Podman Desktop’s default setup provisions a Fedora CoreOS virtual machine, which is immutable/read-only by design. This prevents installing VM-level packages (dnf, system tools, debuggers, etc.) and makes system customization difficult.

This guide uses Lima (limactl) with the official Podman template, which provisions a mutable Fedora Cloud VM that:

  • Is natively supported by Podman Desktop
  • Runs Podman as the container engine
  • Exposes a host-accessible Podman socket
  • Allows VM-level package installation
  • Supports docker / docker-compose via a compatible API

This is the cleanest and officially supported way to use Podman Desktop with a writable VM.


Architecture

macOS
 ├── Podman Desktop (GUI)
 ├── podman CLI (remote)
 ├── docker / docker-compose (optional)
 └── Lima VM (Fedora Cloud, mutable)
      └── Podman engine

Prerequisites

brew install lima podman

(Optional, if you use Docker tooling)

brew install docker docker-compose

Quick Start (Recommended)

Run the interactive setup wizard:

./setup-podman-lima.sh

The wizard will:

  • Ask whether you want rootless or rootful Podman
  • Create a Lima VM (4 CPU / 8 GB RAM / 100 GB disk)
  • Configure the Podman system connection
  • Optionally export and persist DOCKER_HOST
  • Optionally install VM-level packages
  • Leave you with a ready-to-use Podman Desktop engine

Manual Setup (Step-by-Step)

1. Create the Lima Podman VM

Rootless Podman (recommended)

limactl start \
  --name=podman \
  --cpus=4 \
  --memory=8 \
  --disk=100 \
  template://podman

Rootful Podman (only if required)

limactl start \
  --name=podman \
  --cpus=4 \
  --memory=8 \
  --disk=100 \
  template://podman-rootful

This downloads a Fedora Cloud image (mutable, systemd-enabled).


2. Configure the Podman CLI (host → VM)

The Podman template prints the exact commands needed. For reference:

podman system connection add lima-podman \
  "unix://${HOME}/.lima/podman/sock/podman.sock"

podman system connection default lima-podman

Verify:

podman info
podman run quay.io/podman/hello

Podman Desktop will automatically detect this engine.


3. Configure Docker / docker-compose (optional)

Podman exposes a Docker-compatible API via the same socket.

export DOCKER_HOST="unix://${HOME}/.lima/podman/sock/podman.sock"

Test:

docker ps
docker compose version
docker compose build

Persist this in your shell profile if desired.


Entering the VM (SSH / shell)

To enter the VM:

limactl shell podman

This opens an interactive shell inside the VM.

Inside the VM:

  • User: lima
  • OS: Fedora Cloud
  • Package manager: dnf
  • systemd: enabled
  • Filesystem: writable & persistent

Installing VM-Level Packages

Example:

sudo dnf install -y \
  vim \
  tcpdump \
  strace \
  iproute \
  jq

Packages persist across reboots.

You can also run commands non-interactively:

limactl shell podman -- sudo dnf install -y htop

VM Lifecycle Management

limactl stop podman
limactl start podman
limactl delete podman

Filesystem Layout (Reference)

~/.lima/podman/
├── lima.yaml
├── basedisk
├── diffdisk
└── sock/
    └── podman.sock   ← used by podman and docker

Rootless vs Rootful Podman

Mode When to use
Rootless (template://podman) Default, safest, best for dev
Rootful (template://podman-rootful) Needed for some Kubernetes tools, privileged containers

Both run on the same mutable Fedora Cloud VM.


Why This Approach

Requirement Result
Mutable VM ✅ Fedora Cloud
Podman Desktop native support
podman CLI on host
docker / docker-compose
VM-level package installs
No CoreOS immutability

This setup avoids:

  • Fedora CoreOS limitations
  • Podman Machine quirks
  • Colima/Docker compatibility hacks

TL;DR

limactl start template://podman
podman system connection add lima-podman unix://~/.lima/podman/sock/podman.sock
export DOCKER_HOST=unix://~/.lima/podman/sock/podman.sock
limactl shell podman
#!/usr/bin/env bash
set -euo pipefail
# Lima + Podman Template Setup (defaults-first) + simple CLI options
#
# What it does:
# - Installs prerequisites (lima, podman) via brew
# - Creates/starts a Lima VM using template://podman (rootless) by default
# - Configures podman remote connection to unix://~/.lima/<vm>/sock/podman.sock
# - Optionally sets DOCKER_HOST for this session and/or persists it to shell profile
# - Optionally installs VM-level packages via dnf inside the VM
#
# Note: `limactl shell <vm>` is used for interactive SSH; and `limactl shell <vm> -- <cmd>` for remote commands.
usage() {
cat <<'EOF'
Usage:
setup-podman-lima.sh [options]
Options (defaults shown):
--vm-name NAME Lima VM name (default: podman)
--cpus N CPUs (default: 4)
--memory GB Memory in GB (default: 8)
--disk GB Disk in GB (default: 100)
--mode rootless|rootful Podman mode (default: rootless)
--conn-name NAME Podman connection name (default: lima-podman)
--set-docker-host Export DOCKER_HOST for the current shell *via output*
(prints an export line you can eval)
--persist-docker-host Append DOCKER_HOST export to your shell profile
(~/.zshrc, ~/.bashrc, ~/.profile; fish not auto-written)
--install-packages Install default VM packages (see below)
--packages "a b c" Override package list (space-separated)
(implies --install-packages)
--no-brew Skip brew install step (assumes tools already installed)
-h, --help Show this help
Default VM packages (when --install-packages):
vim jq iproute strace tcpdump
Examples:
# Default setup (rootless, 4c/8g/100g), configure podman connection
./setup-podman-lima.sh
# Rootful VM, persist DOCKER_HOST, install packages
./setup-podman-lima.sh --mode rootful --persist-docker-host --install-packages
# Print export you can eval in current shell
eval "$(./setup-podman-lima.sh --set-docker-host)"
# Custom VM name + custom packages
./setup-podman-lima.sh --vm-name podman-dev --packages "vim htop git"
EOF
}
# Defaults
VM_NAME="podman"
CPUS=4
MEMORY_GB=8
DISK_GB=100
MODE="rootless"
CONN_NAME="lima-podman"
NO_BREW=0
SET_DOCKER_HOST=0
PERSIST_DOCKER_HOST=0
INSTALL_PACKAGES=0
PACKAGES_OVERRIDE=""
VM_PACKAGES_DEFAULT=("vim" "jq" "iproute" "strace" "tcpdump")
color() { local c="$1"; shift; printf "\033[%sm%s\033[0m" "$c" "$*"; }
info() { echo "$(color 36 '==>') $*"; }
ok() { echo "$(color 32 '✔') $*"; }
warn() { echo "$(color 33 '⚠') $*"; }
err() { echo "$(color 31 '✖') $*"; }
append_export_to_profile() {
local docker_host_uri="$1"
local shell_name="${SHELL##*/}"
local profile=""
case "$shell_name" in
zsh) profile="${HOME}/.zshrc" ;;
bash) profile="${HOME}/.bashrc" ;;
fish) profile="${HOME}/.config/fish/config.fish" ;;
*) profile="${HOME}/.profile" ;;
esac
if [[ "$shell_name" == "fish" ]]; then
warn "fish shell detected; not auto-writing export syntax."
echo "Add this to ${profile}:"
echo " set -x DOCKER_HOST \"${docker_host_uri}\""
return 0
fi
if grep -Fqs "export DOCKER_HOST=\"${docker_host_uri}\"" "$profile" 2>/dev/null; then
ok "DOCKER_HOST already present in ${profile}"
return 0
fi
{
echo ""
echo "# Added by Lima+Podman setup"
echo "export DOCKER_HOST=\"${docker_host_uri}\""
} >> "$profile"
ok "Appended DOCKER_HOST export to ${profile}"
echo "Open a new shell or run: source \"$profile\""
}
# Parse args
while [[ $# -gt 0 ]]; do
case "$1" in
--vm-name) VM_NAME="$2"; shift 2 ;;
--cpus) CPUS="$2"; shift 2 ;;
--memory) MEMORY_GB="$2"; shift 2 ;;
--disk) DISK_GB="$2"; shift 2 ;;
--mode) MODE="$2"; shift 2 ;;
--conn-name) CONN_NAME="$2"; shift 2 ;;
--no-brew) NO_BREW=1; shift ;;
--set-docker-host) SET_DOCKER_HOST=1; shift ;;
--persist-docker-host) PERSIST_DOCKER_HOST=1; shift ;;
--install-packages) INSTALL_PACKAGES=1; shift ;;
--packages) PACKAGES_OVERRIDE="$2"; INSTALL_PACKAGES=1; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) err "Unknown option: $1"; echo; usage; exit 2 ;;
esac
done
# Validate mode
if [[ "$MODE" != "rootless" && "$MODE" != "rootful" ]]; then
err "--mode must be 'rootless' or 'rootful' (got: $MODE)"
exit 2
fi
TEMPLATE="template://podman"
[[ "$MODE" == "rootful" ]] && TEMPLATE="template://podman-rootful"
SOCK_PATH="${HOME}/.lima/${VM_NAME}/sock/podman.sock"
SOCK_URI="unix://${SOCK_PATH}"
main() {
if [[ "$NO_BREW" -eq 0 ]]; then
info "Installing prerequisites (lima, podman)..."
brew install lima podman >/dev/null
ok "Prerequisites installed"
else
info "Skipping brew install (--no-brew)"
fi
info "Starting Lima VM '${VM_NAME}' with ${TEMPLATE} (${CPUS} CPU, ${MEMORY_GB}GB RAM, ${DISK_GB}GB disk)..."
limactl start \
--name="${VM_NAME}" \
--cpus="${CPUS}" \
--memory="${MEMORY_GB}" \
--disk="${DISK_GB}" \
"${TEMPLATE}" >/dev/null
ok "VM is running"
info "Verifying Podman socket exists: ${SOCK_PATH}"
if [[ ! -S "${SOCK_PATH}" ]]; then
err "Podman socket not found at ${SOCK_PATH}"
echo "Try: limactl shell ${VM_NAME}"
exit 1
fi
ok "Podman socket found"
info "Configuring podman connection '${CONN_NAME}'..."
podman system connection add "${CONN_NAME}" "${SOCK_URI}" 2>/dev/null || true
podman system connection default "${CONN_NAME}"
ok "Podman connection set to default: ${CONN_NAME}"
info "Testing podman connection..."
podman info >/dev/null
ok "podman is connected"
if [[ "$INSTALL_PACKAGES" -eq 1 ]]; then
local pkgs=()
if [[ -n "$PACKAGES_OVERRIDE" ]]; then
# shellcheck disable=SC2206
pkgs=($PACKAGES_OVERRIDE)
else
pkgs=("${VM_PACKAGES_DEFAULT[@]}")
fi
info "Installing VM packages via dnf: ${pkgs[*]}"
limactl shell "${VM_NAME}" -- sudo dnf install -y "${pkgs[@]}"
ok "VM packages installed"
fi
if [[ "$PERSIST_DOCKER_HOST" -eq 1 ]]; then
append_export_to_profile "${SOCK_URI}"
fi
# If requested, print an export line the user can eval in their shell:
# eval "$(./setup-podman-lima.sh --set-docker-host)"
if [[ "$SET_DOCKER_HOST" -eq 1 ]]; then
echo "export DOCKER_HOST=\"${SOCK_URI}\""
fi
echo
ok "Done."
echo "Podman socket: ${SOCK_URI}"
echo "Enter VM: limactl shell ${VM_NAME}"
echo "Docker compat: export DOCKER_HOST=\"${SOCK_URI}\""
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment