How to manage containers with podman/docker?

I have a BananaPi R4 with 4GB ram and since I have no other SBC plan to use it as a container host for Grafana such to monitor my system beyond LuCi and beyond the router.

The podman setup itself is done. It is working. Separate subnet and DNS and such. Storage on the NVMe.

What I'm looking for now is how to setup them running automatically. I saw the wiki about this, but having it in rc.local looks weird.

I debated with GitHub Copilot about it and it suggested using procd and having a script in /etc/init.d/containers that looks something like this:

/etc/init.d/containers
#!/bin/sh /etc/rc.common

START=100
STOP=20
USE_PROCD=1
NAME=containers
PROG=/usr/bin/podman

start_service() {
    procd_open_instance "$NAME"

    # Start Grafana without port mapping
    $PROG run -d --name=grafana \
        --network bridge \
        custom-grafana:latest
    procd_set_param respawn

    procd_close_instance
}

stop_service() {
    $PROG stop grafana 2>/dev/null
    $PROG rm -f grafana 2>/dev/null
}

The web searches and forum search didn't return anyone describing their setup. I guess it makes sense, OpenWrt is not really meant to be a container host.

But if there is someone here reading this with a running setup, care to share it?
How do you manage your containers?
Do you use an init.d script for all of them combined or each service their own script?
Or even docker compose? (Which doesn't come included with podman and I'd rather not put a whole python installation in my image)

This is what I came up with. Feedback welcome.
Now uses UCI /etc/config/containers to determine the containers to start. Each running as their own instance inside procd.

#!/bin/sh /etc/rc.common

START=100
STOP=20
USE_PROCD=1
NAME=containers
PROG=/usr/bin/podman

. /lib/functions.sh

create_container() {
	local cfg="$1"
	local name envs image cap_add

	config_get name "$cfg" name "$cfg"

	$PROG container exists "$name" && {
		logger -t "$NAME" "Container $name already exists, skipping creation"
		return 0
	}

	config_get image "$cfg" image
	config_get cap_add "$cfg" cap_add

	append_env() {
		envs="$envs -e $1"
	}
	config_list_foreach "$cfg" env append_env

	logger -t "$NAME" "Creating container $name with image $image"
	$PROG create --name="$name" \
		${cap_add:+--cap-add="$cap_add"} \
		$envs \
		"$image" || {
		logger -t "$NAME" -p err "Failed to create container $name"
		return 1
	}

	return 0
}

start_container() {
	local cfg="$1"
	local enabled name

	config_get enabled "$cfg" enabled 0
	[ "$enabled" -eq 0 ] && return 0

	create_container "$cfg" || return 1

	config_get name "$cfg" name "$cfg"
	logger -t "$NAME" "Starting container $name"

	procd_open_instance "$name"
	procd_set_param command "$PROG" start -ai "$name"
	procd_set_param respawn
	procd_close_instance
}

stop_container() {
	local cfg="$1"
	local name

	config_get name "$cfg" name "$cfg"

	logger -t "$NAME" "Stopping container $name"
	$PROG stop "$name" 2>/dev/null
}

start_service() {
	config_load containers
	config_foreach start_container container
}

stop_service() {
	config_load containers
	config_foreach stop_container container
}
1 Like

It sounds quite powerful as a solution, not spoiling resouces for a supervisor like proxmox for example.
I wonder what are the most relevant differences, as I'm not educated on the subject, but I'm looking at this thread with much interest.