Skip to content

Installing Podman for managing containers and running container with dynamic DNS client for Cloudflare

Introduction

Podman is a Docker alternative with distinction that it is a daemonless container engine and containers created with Podman can be run by regular users without root privileges. This enhances security because if a process escapes container, it won't run on the host with root privileges but with user privileges running the container. If it's so secure, why is Docker still popular? The answer is that the daemonless architecture of Podman makes it more complex to manage, especially set up containers to run with every system start. Also there is less documentation available for Podman compared to Docker, so this post will try to summarize how to create functioning Podman host.

OS used: Debian 12
Software used: Podman 4.3.1

Source

Prerequisites

Create new virtual machine for example using this tutorial Fast initialization of Debian VM using Ansible.

Podman installation

Podman is very easy to install because it's in Debian repository. Podman also has podman-compose (separate package in Debian) as an alternative to docker-compose, but in newer version of Podman (> 4.4) quadlets are recommended instead of podman-compose. Debian 12 includes Podman 4.3, which is why we will use standard Podman syntax. Not all the options that we will be utilizing are available within podman-compose.

  • Install packages:
$ sudo apt install podman
  • Next, because Podman is run as normal user, command should be used to ensure that a user session for your user is spawned at boot and kept active even after logouts from GUI or tty sessions:
$ sudo loginctl enable-linger <username>

<username> - enter username of user using Podman

  • Reboot system for above command to take effect.

Using Podman

In a homelab environment, people often have dynamic external IPs assigned by their Internet service provider. If someone wants to run a web application on a homelab server, some form of dynamic DNS service should be used. This post will demonstrate how to run the inadyn dynamic DNS client with Cloudflare using Podman: how to build new image with inadyn package, create configuration for Cloudflare, and then run a container based on that image.

Podman accepts Docker naming of files, so we can use Dockerfile or Containerfile for files with image definition. In this tutorial I will use Docker-style naming of files because many text editors, like VS Code, have Docker syntax autocompletion for Dockerfile, which helps in writing these files.

Creating image

  • Create folder for Podman files:
$ folderpath=/mnt/inadyn-podman && \
  sudo mkdir -m 700 ${folderpath} && \
  sudo chown <username>:<username> ${folderpath}

-m - set folder permissions
<username> - enter username of user using Podman

  • Create image file. New image will be based on Debian image:

$ vim /mnt/inadyn-podman/Dockerfile
/mnt/inadyn-podman/Dockerfile
FROM docker.io/debian:bookworm
LABEL description="My custom inadyn image, based on Debian, made to learn Podman"

# Install required packages, then clean package cache for smaller image size
RUN apt update && \
    apt install -y inadyn ca-certificates && \
    apt clean && \
    apt autoremove --purge && \
    rm -rf /var/lib/apt/lists

RUN mkdir -m 770 /etc/inadyn && chown root:debian-inadyn /etc/inadyn

# debian-inadyn user is automatically created when installing inadyn
USER debian-inadyn

RUN touch /etc/inadyn/inadyn.conf && chmod 600 /etc/inadyn/inadyn.conf

ENTRYPOINT ["/usr/bin/inadyn", "--foreground", "-f", "/etc/inadyn/inadyn.conf"]

Note

If you want to use containers from Docker Hub in Podman you need to put docker.io/ before image name. Otherwise, images from Red Hat's quay.io register are used.

  • Build image:
$ podman build -t inadyn -f /mnt/inadyn-podman/Dockerfile .

-t - image tag
-f - path to Dockerfile
. - dot is important at the end of command

  • Check that image was built:
$ podman images

Creating Cloudflare inadyn configuration

Cloudflare

I assume you have your domain set up in Cloudflare. If not, follow these steps:

  • In Cloudflare dashboard enter your site
  • Click DNS > Records > click Add record:
  • Create A DNS record:
    • Type: A
    • Name: @
    • IPv4 address: 127.0.0.1 - address entered here doesn't matter because it will be changed by inadyn and will point to your IP address
    • Proxy: disable - can't be enabled for simple IP like 127.0.0.1. It will be enabled by inadyn client when public IP is obtained. When this setting is enabled, Cloudflare will proxy traffic going to your server and IP of your server will be hidden behind Cloudflare servers IP.

Podman host

  • Create a folder for mounting inadyn configuration into the container:
$ folderpath=/mnt/inadyn-conf && \
  sudo mkdir -m 700 ${folderpath} && \
  sudo chown <username>:<username> ${folderpath}

-m - set folder permissions
<username> - enter username of user using Podman

  • Create inadyn config file with appropriate permissions:

$ filepath=/mnt/inadyn-conf/inadyn.conf && \
  touch ${filepath} && \
  chmod 600 ${filepath} && \
  sudo vim ${filepath}
/mnt/inadyn-conf/inadyn.conf
# How often the IP is checked. The value denotes seconds
period = 300

# Cloudflare provider configuration
provider cloudflare.com {
    username = mydomain.com
    password = API_TOKEN # Create a unique custom api token with the following permissions: Zone.Zone - Read, Zone.DNS - Edit.
    hostname = mydomain.com
    proxied = true # set it to true if you want Cloudflare Proxy enabled otherwise set to false (false is default), this setting will change your setting on Cloudflare webpage
}

mydomain.com - change it to your domain
API_TOKEN - replace it with your generated Cloudflare API token, below is instruction how to generate one:

  • Generate Cloudflare API Token
  • Click Avatar in right top corner > My Profile > API Tokens > click Create Token > Create Custom Token:
  • Token name - enter name for your Token
  • Permissions - create 2 entries and set following values in fields:
    • Zone | Zone | Read
    • Zone | DNS | Edit
  • Zone Resources - set following
    • Include | Specific zone | chose your zone created earlier
    • Example: Include | Specific zone | mydomain.com
  • Client IP Address Filtering - if you have dynamic Public IP you can leave it empty
  • TTL - leave empty for Token to be valid forever
  • Click Continue to summary > click Create Token
  • Copy Token - Token will be shown only once, if you loose it, delete it and generate new Token

Creating container

  • Create container using following command:
$ podman run -d \
         --name inadyn \
         --userns=keep-id:uid=100,gid=101 \
         --volume /mnt/inadyn-conf:/etc/inadyn \
         localhost/inadyn

-d - run container in detached mode (detach from it after starting and run in background)
--userns=keep-id:uid=100,gid=101 - map your user to user inside container for proper volume permissions, debian-inadyn user inside container have UID=100, GID=101
--volume /host/path:/container/path - mount folder from host inside container
localhost/inadyn - image used for container

Debugging when some problems occur

If something is not working, you can use the following commands to debug.

  • View container logs:
$ podman logs inadyn
  • Logs are also stored in systemd journal so you can view history of your IP changes:
$ journalctl --user --grep "IP#"

--user - show messages from user services
--grep - filter output entries (similar to grep command)

  • Check permissions of folder with config file. It should be owned by user runnig Podman:
$ ls -lAF /mnt/ /mnt/inadyn-conf/

-l - view files as list
-A - don't show . and .. in list
-F - show / at the end of folders and * at the end of executable files

  • Display user UID inside of the container and user UID of the host user running Podman:
$ podman top inadyn user huser

Result should be:
USER           HUSER
debian-inadyn  <UID of the host user running Podman>

Running container on every system boot

Podman uses systemd to run containers. So in order to run container when system starts, you need to create systemd service file. Fortunately, Podman has a tool for automatically generating such files.

  • First create folder for your user to store systemd files (because Podman is run as a regular user):
$ mkdir -p ~/.config/systemd/user
  • Next generate systemd service and write it to file:
$ podman generate systemd --new --name CONTAINER_NAME > /path/to/file.service

$ podman generate systemd --new --name inadyn > ~/.config/systemd/user/inadyn.service
  • After that reload user's systemd unit list and enable the newly generated service:
$ systemctl --user daemon-reload && \
  systemctl --user enable inadyn.service && \
  systemctl --user start inadyn.service

Note

By default Podman systemd container service is generated with --restart-policy=on-failure.

  • Check if service is running:
$ systemctl --user status inadyn.service
  • Reboot system and check if your container will be running.

Appendix - Podman and network ports

In Linux regular users can't open ports below 1024. So if you want to run Podman container that listens on port for example 80 you need to run following command:

$ sudo bash -c "echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/podman_unprivileged_port_start.conf"

This command specify lowest unprivileged port, so user can access all ports above. For example in above command setting unprivileged port to 80 also means that 443 is accessible.

Reboot the system afterward. Remember to also open the required ports in your firewall.