My first container using Podman
One of the reasons I got myself a home server is so that I can learn about containers. I've just got my first one set up, and as this was my first 'rodeo' it certainly took a while, but it turned out to be really quite easy in the end. So, here's how I did it, and just as importantly - also what I learned.
podman and quadlet
I decided that I wanted to see if I could run a Minecraft server on my home server - because, why not? I've never had anything to do with containers before. However, I'm not an expert, but looking through the MicroOS docs I knew that I needed to use podman, which is like Docker, and that it came pre-installed on MicroOS. Later on, I realized that there was another app called quadlets - it's now deprecated and merged into Podman since version 4.4 - that has really good functionality that I should use.
Historically, quadlets allowed you to wrap a systemd service file around your containers. To be clear, this means you can start your containers using the standard systemctl start minecraft.service
command. This is so cool! Podman used to allow you to run the now deprecated command podman generate systemd
, before quadlets was merged to be standard podman functionality.
Creating a podman container
Using podman for the first time - without trying to wrap it into a systemd unit file - is actually quite tricky as there's quite a lot of syntax to get right! Here's my podman command that I used to create a podman container:
sudo podman run -d --replace --name minecraft --network host --memory=6g --memory-reservation=4g -v /var/lib/minecraft:/data:Z -e EULA=TRUE -e TYPE=PAPER -e MEMORY=6G -e ALLOW_FLIGHT=TRUE -e MODE=creative --user 1000:1000 docker.io/itzg/minecraft-server:latest
Let's work our way through this:
- Base command:
sudo podman run
- Run a container with root privileges
- Container options:
-d
- Detached mode (i.e., run in background)--replace
- Replace any existing container with the same name--name minecraft
- Name the container "minecraft"--network host
- Use host's network directly (so no network isolation)
- Resource limits:
--memory=6g
- Hard limit of 6GB RAM--memory-reservation=4g
- Soft limit of 4GB RAM - n.b. the container tries to stay under this.
- Storage:
-v /var/lib/minecraft:/data:Z
- This volume mount has three parts separated by colons:Source path:
/var/lib/minecraft
- This is the directory on my home server that I want to make accessible to the container - the docker image will be downloaded and installed here.
Destination path:
/data
- This is where the contents e.g., maps etc. will appear inside the container
- Applications inside the container will read/write to this path
SELinux label option:
Z
- The uppercase
Z
tells Podman to relabel the content with a private unshared SELinux label - This ensures the container has full access to the content
- This is particularly important on MicroOS - and any other distros which use SELinux
- The
Z
flag is different from lowercasez
, which indicates that the volume is shared among multiple containers.
- The uppercase
This volume specification allows persistent data to survive container restarts, as any data written to /data
inside the container is actually stored in /var/lib/minecraft
on your host system.
- Environment variables
This is how you configure your container, which will pass some of these instructions through to the minecraft server software:
-e EULA=TRUE
- Accept Minecraft EULA-e TYPE=PAPER
- Use PaperMC server implementation-e MEMORY=6G
- Allocate 6GB to Java process-e ALLOW_FLIGHT=TRUE
- Allow flying in-game-e MODE=creative
- Set default game mode to creative
- User context:
--user 1000:1000
- Run as UID:GID 1000 i.e., a regular user inside the container
- Image specification:
docker.io/itzg/minecraft-server:latest
- Pull latest version of this image from Docker Hub. Yes, Podman allows you to access all the docker.io images!
So, if you were to type the above command at the Linux command prompt then you'll get yourself a Minecraft server that runs as a container! But that's about all it will do - once you reboot your MicroOS server you'll lose your container, and you'll need to type the above command once again to get everythi ng set up again in the way you need it to. If only there was a way that containers could be handled in a more robust way.
Setting up a container as a systemd-service
Well, there is, and this is where the old quadlet functionality being merged into podman starts to come into its own.
Looking at the way the podman command syntax above was run, you'll see it's run under sudo
. This obviously means that we want the container to be run as root. There are two different ways to run a container, either as root
or rootless
. When I first came across the term rootless
it was so confusing to me. To be clear then, if you want to run the container under a user/system account, then you need to be running the container rootless
; because, you know, saying that the container is being run under a user, or system account is probably too simple!
In this example, we're going to be running the container as root
. I did mistakenly try to run the container as my user-id, and I got into all sorts of issues. So I need to spend some additional time trying to figure out how to get this to work under a system account i.e., a rootless
container.
As we're going to be running this container as root
we can only have a systemd service file in one of these two locations:
/usr/share/containers/systemd/
/etc/containers/systemd
If, like me you initially tried to create a systemd service file here:
~/.config/container/systemd
It will automatically become a rootless
container, and I believe that requires a slightly different configuration to what I've shown in this blog post. This at least is what tripped me up, when I was trying to get my container to run initially.
So let's create a minecraft.service
file in /etc/containers/systemd
- which to me, at least, is the most logical of the 2 paths to put it.
So using your favorite Linux text editor, you need to use the following text in the systemd minecraft.service
file:
[Container]
ContainerName=minecraft
Image=docker.io/itzg/minecraft-server:latest
PodmanArgs=--memory=6g --memory-reservation=4g --network=host -e EULA=TRUE -e TYPE=CUSTOM -e MEMORY=6G -e ALLOW_FLIGHT=TRUE -e MODE=creative
PublishPort=25565:25565
Volume=%h/.local/share/minecraft:/data:Z
AutoUpdate=registry
[Install] WantedBy=default.target
By creating a systemd service file then I think that it makes things easier to understand. If you're familiar at all with systemd services then the [Install]
should be familiar to you, but the [Container]
section should be new.
Let's go through the [Container]
section together:
ContainerName=minecraft
-Sets the name of the container to "minecraft", which is how you'll identify it in commands likepodman ps
.Image=docker.io/itzg/minecraft-server:latest
- Specifies the container image to use - pulling the latest version of the itzg/minecraft-server image from Docker Hub. This is a popular image for running Minecraft servers.
PodmanArgs=--memory=6g --memory-reservation=4g --network=host -e EULA=TRUE
- Sets additional parameters for the container:
- Limits maximum memory usage to 6GB
- Sets a soft memory reservation of 4GB
- Uses the host's network stack (directly exposing the container on your network)
- Accepts the Minecraft End User License Agreement
-e TYPE=PAPER -e MEMORY=6G -e ALLOW_FLIGHT=TRUE -e MODE=creative
Additional environment variables for the Minecraft server:- Creates a papermc server
- Allocates 6GB RAM to the Java process
- Enables player flight capability
- Sets the default game mode to creative
PublishPort=25565:25565
- Maps the container's port 25565 (Minecraft's default port) to the same port on the host system.
Volume=%h/.local/share/minecraft:/data:Z
Maps a directory for persistent storage: -%h
is a variable representing your home directory. Note: ~ does not work! -Maps.local/share/minecraft
in your home to/data
in the container -The:Z
suffix handles SELinux labeling for proper permissionsAutoUpdate=registry
Enables automatic container image updates by checking the registry for newer versions.
Once you've saved the above, to run this container/systemd service file you simply need to issue the following commands:
systemctl daemon-reload
systemctl start minecraft
To check that it's working you can use the following command:
systemctl status minecraft
So, to get to this stage it took me about 4-5 hours of trying to figure out how to set everything up! The biggest thing that tripped me up was trying to run the systemd service file from ~/.config/container/systemd/
. As soon as I moved the .service file to /etc/containers/systemd/
it literally worked first time! While I've never used Docker, or Kubernetes, etc., I am familiar with systemd, so for me this process simply makes the most sense. Now that I know what I need to do, I do think that next time I should be able to have a container running a lot quicker.