Podman
OpenC3 COSMOS Using Rootless Podman and Docker-Compose
These directions are for installing and running COSMOS using Podman instead of Docker. If you have Docker available, that is a simpler method.
Podman is an alternative container technology to Docker that is actively promoted by RedHat. The key benefit is that Podman can run without a root-level daemon service, making it significantly more secure by design, over standard Docker. However, it is a little more complicated to use. These directions will get you up and running with Podman. The following directions have been tested against RHEL 8.8, and RHEL 9.2, but should be similar on other operating systems.
NFS does not work for holding container storage due to issues with user ids and group ids. There are workarounds available but they all involve moving container storage to another location: either a different partition on the host local disk, or into a special mounted disk image. See: https://www.redhat.com/sysadmin/rootless-podman-nfs. Note that there is also a newish Podman setting that allows you to more easily change where the storage location is in /etc/containers/storage.conf called rootless_storage_path. See https://www.redhat.com/sysadmin/nfs-rootless-podman
How COSMOS Detects Podman
openc3.sh detects your container runtime and whether it is running rootless, so there is nothing to edit in compose.yaml:
- It uses
dockerif present, otherwisepodman. - For the compose command it tries
<runtime> composefirst and falls back to the standalonedocker-compose.podman-composeis not supported - installdocker-compose(note thatpodman composealso delegates todocker-composewhen it is installed). - It runs
<runtime> infoand looks forrootless. When rootless Podman is detected it exportsOPENC3_USER_ID=0andOPENC3_GROUP_ID=0; otherwise it exports your actualid -uandid -g. Either way those values feed theuser: "${OPENC3_USER_ID:-1001}:${OPENC3_GROUP_ID:-1001}"lines incompose.yaml. See User ID and Group ID Mapping below for why rootless gets0:0.
User ID and Group ID Mapping
Under rootless Podman the containers run as 0:0. That is root inside the container, not root on the host. Podman starts the containers in a user namespace where container uid/gid 0 maps back to the unprivileged host user who ran openc3.sh, so:
- Files the containers write into bind mounted host volumes - logs, buckets, plugin installs - come out owned by your host user, with your uid and gid, rather than by an unusable high numbered subuid.
- The containers hold no privilege on the host that you do not already have yourself.
Every service in compose.yaml also sets read_only: true, so being root inside a container gains little beyond reading files in the image that a non-root user could not. It cannot modify the image.
Under Docker, or under rootful Podman, there is no user namespace doing this remapping. openc3.sh exports your real uid and gid instead, and the container processes run as that uid directly. Files on bind mounted volumes still end up owned by you - the difference is that the container is running as your uid outright, rather than running as container root that Podman then maps to your uid.
Older versions of this guide told you to uncomment user: 0:0 lines in compose.yaml, that has been removed. openc3.sh now handles the user ids automatically. Put any customization you need in compose.override.yaml (non-secret settings) or .env.local (passwords and keys) instead - see Docker Compose.
Redhat 8 and 9 Instructions
-
Install Prerequisite Packages
Note: This downloads and installs the latest docker-compose release from Github. If your operating system has a docker-compose package, it will be easier to install using that instead. RHEL8 does not have a docker-compose package.
sudo yum updatesudo yum install git podman podman-docker netavarkcurl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o docker-composesudo mv docker-compose /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-composesudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose -
Configure Host OS for Redis
sudo suecho never > /sys/kernel/mm/transparent_hugepage/enabledecho never > /sys/kernel/mm/transparent_hugepage/defragsysctl -w vm.max_map_count=262144exitNote that these are not persistent across reboots. To make
vm.max_map_countpermanent:sudo sh -c 'echo "vm.max_map_count=262144" > /etc/sysctl.d/99-openc3.conf' -
Configure Podman to increase the PID limit
sudo cp /usr/share/containers/containers.conf /etc/containers/.sudo vi /etc/containers/containers.confThen edit the
pids_limitvalue to -1 (unlimited) -
Configure Podman to use Netavark for DNS
sudo sh -c 'echo ip_tables > /etc/modules-load.d/ip_tables.conf'sudo vi /etc/containers/containers.confThen edit the network_backend line to be "netavark" instead of "cni"
Note that Podman 5 uses netavark by default, so on a current RHEL 9 install the
network_backendline may already be correct or absent. Verify withpodman info --format '{{.Host.NetworkBackend}}'.
The above "echo ip_tables" line is added because RHEL 9.x uses nftables by default. The legacy ip_tables kernel module is not guaranteed to be loaded at boot — particularly on cloud images such as AWS EC2. However, netavark still requires ip_tables to implement NAT and forwarding for rootless Podman containers. Rootless users cannot load kernel modules, so if ip_tables is missing, netavark networking fails silently. If using rootless Podman with netavark ensure the ip_tables kernel module is preload.
File Descriptor and Memory Limits
COSMOS runs many processes across the containers and rootless Podman cannot raise a limit above the hard limit of the user that started it. Check your limits:
ulimit -a
If open files (ulimit -n) is low, raise it - typically by adding a line to /etc/security/limits.d/ (for example * hard nofile 1048576) and logging back in. See also the Troubleshooting page.
The openc3-tsdb service in compose.yaml requests nofile of 1048576 and unlimited memlock. If your host cannot grant those, the container fails to start with an error about setting rlimits. Lower them in compose.override.yaml to something your system allows:
services:
openc3-tsdb:
ulimits:
nofile:
soft: 262144
hard: 262144
memlock:
soft: 65536
hard: 65536
These values are system specific - use the largest values your host allows.
Configure the Podman User
The following instructions must be performed for each user using COSMOS. Individual podman users store their own container image in their local home directory. This is especially important if you're in an airgapped environment as you will need to load the containers for each user.
-
Start rootless podman socket service
systemctl enable --now --user podman.socket -
Put the following into your .bashrc file (or .bash_profile or whatever)
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock" -
Source the profile file for your current terminal
source .bashrc -
Get COSMOS - A release or the current main branch (main branch shown)
git clone https://github.com/OpenC3/cosmos.git -
Optional - Set Default Container Registry
If you don't want podman to keep querying you for which registry to use, you can create a $HOME/.config/containers/registries.conf and modify to just have the main docker registry (or modify the /etc/containers/registries.conf file directly)
mkdir -p $HOME/.config/containerscp /etc/containers/registries.conf $HOME/.config/containers/.vi $HOME/.config/containers/registries.confThen edit the unqualified-search-registries = line to just have the registry you care about (probably docker.io)
-
Customize your deployment
Do not edit
compose.yamlor.env-openc3.shmergescompose.override.yamlon top ofcompose.yamland loads.env.localafter.env, so your changes survive a COSMOS upgrade. See Docker Compose for the full details. Common Podman changes:- By default Traefik only listens on
127.0.0.1:2900and127.0.0.1:2943. To allow access from other machines, republish the ports incompose.override.yamlwithout the127.0.0.1prefix. Rootless Podman cannot bind privileged ports, so choose a port above 1023 (or setnet.ipv4.ip_unprivileged_port_start). Make sure your firewall allows the port you choose. - To allow HTTP (rather than HTTPS) connections, set
OPENC3_ALLOW_HTTP=1under theopenc3-traefikservice'senvironment:incompose.override.yaml. For a real deployment use an SSL config instead - see SSL-TLS. - Set
OPENC3_EXTERNAL_URLin.env.localto the URL users will browse to.
services:openc3-traefik:ports:- "2900:2900"- "2943:2943"environment:- OPENC3_ALLOW_HTTP=1compose.override.yaml cannot be empty under PodmanCOSMOS ships
compose.override.yamlwith everything commented out. Docker Compose accepts that, but a Podman build rejects a compose file with no content. If you hit an error parsing the override file, either delete it or give it a single valid key:services: {} - By default Traefik only listens on
-
Run COSMOS
cd cosmos./openc3.sh run -
Wait until everything is built and running and then goto http://localhost:2900 in your browser
Podman can also be used on MacOS, though we still generally recommend Docker Desktop
MacOS Instructions
-
Install podman
brew install podman -
Start the podman virtual machine
podman machine initpodman machine startpodman machine startprints theDOCKER_HOSTvalue to use - the socket path depends on your username and the machine's VM provider. Copy it from that output, or derive it:export DOCKER_HOST=$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}' | sed 's|^|unix://|') -
Install docker-compose (Optional if you already have Docker Desktop)
brew install docker-compose -
Run COSMOS
cd cosmos./openc3.sh runIf a container fails to start because of an SELinux relabel error on a bind mount, remove the
:zsuffix from that mount by redeclaring it incompose.override.yaml. Compose merges volumes by their container-side path, so listing the same target without:zreplaces the original:services:openc3-traefik:volumes:- "./cacert.pem:/devel/cacert.pem"- "./openc3-traefik/traefik.yaml:/etc/traefik/traefik.yaml"