Years ago I was having lunch with a friend, a much more experienced software engineer than me, and, among many, one of the topics of discussion was Docker and containerization. Very vividly I remember one of his remarks about it, I will try to paraphrase it: “… if I need to run an npm project/script for something on my computer, I don’t want to have tons of garbage dependencies installed along with it …” Bear with me for a second, my goal is not to start another tool warfare between developers.
Over the coming years I fell in love with Docker and containerization myself. Also, being in the industry, you cannot avoid working with Node.js and npm as well and I hold no grudges in general against it at all, a tool is a tool. But I cannot help the fact that his remark stuck with me for years on, and finally, a couple of weeks ago, I got the opportunity to turn it into something palpable, in an open source project.
Some months ago my friends introduced me to a roguelike genre of games with a game called Balatro. Great stuff, go look it up. I got hooked to say the least. Fast-forward a month or so, another friend in an unlikely situation introduced me to the project, a game called PokeRogue. From their site:
“PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, fighting trainers, bosses, and more!”
He tutored me a bit on how to get better faster and soon we were gaming together. One day he mentions how the project is open source and if, since he knew I was working with computers, I could maybe look into it and ‘hack’ it for him to has some extra advantage against the game from the start, just to make some things easier hehe.
Sure enough, curious as I am, I jumped to their GitHub project page and realized it is a very well maintained project with an active team and some very serious department heads. One thing caught my eye as well, they didn’t have a Dockerfile. Here is the thing about me, I don’t develop without Dockerfile.
And there it was, a perfect opportunity to help the project I’m a fan of with a skill set I already possess and, of course, to show off to my friends hehe.
Dockerization begins
There was already some attempts to do this, but they were mostly outdated and not working. I forked the project and in a couple of hours I tested and commited the first version of the Dockerfile being aware that this was by no means something ready to be merged to the main project. It was more of a show of interest, that there is someone who can containerize their project and maybe help others who also like to use containers in development.
However, a plot twist happened when I jumped on their Discord channel and noticed months old discussion about how they are against using Docker. They clarified to me they are not inherently against containerization but that they have some gripes against root-full containers so they suggested using Podman instead.
Podmanization begins
Another great opportunity was there because I was not as proficient in using Podman as I would have liked.
Few days had passed and I had again committed a more robust Dockerfile to be used with Podman, but there was a challenge to it. With Docker you would build an image, mount the working directory and run the game in development hot reload mode and everything worked. There is more to it, especially later when it comes to being completely independent of dependencies but in general it was ‘working’.
docker run -p 8000:8000 -v "$(pwd)":/app --name pokerogue-container pokerogue pnpm start:dev --host
# You would build an image, mount the working dir and run the command
With Podman the first iteration was missing a key thing, I could only build an image and run it in order to play but since I had a couple of issues with mounting the current directory and user permissions, I left it in a state where you would be able to use Podman just to play the game. This is what I had:
1. `podman build -t pokerogue -f Dockerfile .`
2. `podman run --rm -p 8000:8000 localhost/pokerogue`
3. Visit `http://localhost:8000/`
You could play, but still had to have dependencies and submodules installed locally (more on that later), far from ideal.
User permissions and pnpm and vite
Project uses vite to compile the code and vite uses node_modules folder to store cache and whatnot. It took me some time, but I figured that Podman has a command that would preserve the permissions from the host inside the container:
podman run --rm -p 8000:8000 -v $(pwd):/app:Z --userns=keep-id -u $(id -u):$(id -g) localhost/pokerogue
There was another thing that had write permission issues which was when you run the project with constrained pnpm version (constrained in Dockerfile).
It wanted to write package manager constraint to package.json which would automatically stage it for commit - minor nuisance for potential developers
but just don’t commit files you didn’t change yourself. The team suggested I commit that file myself despite the fact that
I didn’t like to mess with original project files, but we settled on it quickly.
node_modules/ and overwriting empty directories
I already mentioned that in order for this to work, you had to have node_modules, and some other assets present on your local machine. I noticed this when testing on a fresh machine/repo. On fresh repo, the assets and node_modules would be overwritten with empty directories and the project wouldn’t run. This was a big deal because the whole point of this is to be able to run the project without having the node, npm or whatever else installed locally, to rely on only your container engine, podman in this case.
The solution I found and was happy with was to build an image, create a temporary volume, copy the things you need from host to container, and then mount everything back. There may be better solutions but this was one that uses only podman commands and in the end works everything works as expected. Probably not if you are working with those assets or installing dependencies from the container, but that is out of scope for now. I was happy containerizing the project like so:
1. `podman build -t pokerogue -f Dockerfile .`
2. `podman create --name temp-pokerogue localhost/pokerogue`
3. `podman cp temp-pokerogue:/app/node_modules ./`
4. `podman cp temp-pokerogue:/app/public/locales ./public/`
5. `podman rm temp-pokerogue`
6. `podman run --rm -p 8000:8000 -v $(pwd):/app:Z --userns=keep-id -u $(id -u):$(id -g) localhost/pokerogue`
7. Visit `http://localhost:8000/`
The contributor
Although I am not official contributor (hehe) until this gets merged to official repository, I am very happy with how this journey went. I definitely learned more about Podman and I liked how the team communicated with me. While what I think are the final reviews are still to be checked, even if they reject it, this is still a success in my book. There very well may be more than two people who like to have their projects containerized.
Photo by Julia Taubitz on Unsplash