Containers for Dev
By Anatoly Mironov
Containers are awesome not only for hosting apps, but also for local development. In this post I am just braindumping my notes from my recent labs. I have a couple of use cases, completely independent from each other.
- Hugo Blog Authoring
- Vite App Generation and Development
- Minecraft Server on a Raspberry Pi
- Dev Container and Github Codespaces
Podman
I am running podman
instead of docker
. I am not choosing it for a particular reason, it’s more out of curiosity, but if you want to dig deeper in the comparison between those two, I would recommend this blog post:
The best part of podman
vs docker
is that in almost all commands you just replace docker with podman, or the other way around. This time I am only using podman
.
Installing podman
On Windows the best way to install podman is:
winget install -e --id RedHat.Podman
wsl --install --no-distribution
podman machine init
podman machine start
Hugo Blog Authoring
My personal blog is built with Hugo. I installed Hugo on my old Mac using brew and it works fine. On my Windows computer I thought, after new installs and so, maybe instead of installing it directly in the OS, I could just spin up a container. That way I would be able to get it up and running after a re-install, or even on a new computer.
For this I just started a new container.
podman run -it --name hugo -v .:/app -p 1313:1313 debian:bookworm-20250407-slim bash
Let’s break it down:
command part | meaning |
---|---|
podman run | spin up a container |
-it | run it interactively |
–name hugo | refer to it as “hugo” later |
-v .:/app | use the current folder inside the container under /app |
-p 1313:1313 | expose and map the default http port for Hugo |
debian:bookworm-20250407-slim | use debian slim image with the specific tag |
bash | jump in directly to bash in terminal |
When it comes to the image tag, I prefer to use specific tags and avoid latest
or similar tags, to make sure I know what I use to make troubleshooting easier in the future.
# this is how you can stop the container
podman stop hugo
# this is how you start it and jump into its terminal
podman start -ai hugo
# this is how you get rid of it
podman rm hugo
Inside the container, run once to install Hugo:
apt update && apt upgrade -y
apt install curl -y
cd /tmp
# find the right version for you on
# https://github.com/gohugoio/hugo/releases
curl -sLO https://github.com/gohugoio/hugo/releases/download/v0.146.4/hugo_extended_0.146.4_linu
x-amd64.deb
dpkg --install hugo_extended_0.146.4_linux-amd64.deb
Inside the container run this to start Hugo Blog
cd /app
hugo server -D --bind 0.0.0.0
Now in your computer, navigate to localhost:1313
and try out your blog site.
You can obviously build your own image for Hugo, like TeknikUglen/podman-hugo and lowply/build-hugo, or use an image from dockerhub like klakegg/hugo. But I find really convinient to just create a container from scratch and re-use it.
Some additional points:
--bind 0.0.0.0
in hugo command is important, otherwise you won’t access it from your host computer.- automatic page reload does not work, or I haven’t figured out how to fix when served from a container, it doesn’t bother me though
- git commands are executed from the host computer, I don’t want install, configure and authenticate git, I want to tear up and tear down a container easily
- a
slim
container image is fast and storage efficient, it’s less than 100 MB and it still has all the essential tools like apt in place debian
is my personal favorite, maybe because I used its derivative ubuntu a lot back in the days and also because all the tooling is familar from Azure DevOps Pipelines and Github Actions.
Vite App Generation
Next thing I want to share is a Vite Container. Instead of installing all the dependencies on your host computer, you can spin up a Vite container and generate your site like this:
mkdir vite-lab
cd vite-lab
podman run -it --name vite-dev -v .:/app -p 5173:5173 node:latest bash
Inside the container run this to install vite and scaffold an app in the /app
folder
cd /app
npm install -g vite
npm create vite@latest .
In package.json adjust: “dev”: “vite –host 0.0.0.0” so that the site is accessible from your computer.
Start the site:
npm run dev
Open the site under localhost:5173
.
If you want a live reload, add watch->usePolling in vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
watch: {
usePolling: true, // Enable polling
interval: 100, // Set polling interval (in milliseconds)
},
},
});
Building an image
Having just container is fine, but if you want to speed up the process of spinning up a container, or share it with your peers, the next step is to define a Dockerfile and build an image.
Here is my simple Dockerfile.dev
:
FROM node:22.14.0
RUN npm install -g vite
WORKDIR /app
It doesn’t even justify an own image, but it’s a start.
Builder and Runner
This is also overkill for this little example but I found it appealing to divide deployment in two stages: Build and Run.
If you look at the Dockerfile
copied from How to Dockerize Vite:
FROM node:23.11.0-alpine3.21 AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
FROM busybox:1.37.0 AS runner
WORKDIR /app
COPY --from=builder /app/dist .
CMD ["busybox", "httpd", "-f", "-v", "-p", "8080"]
YOu can see that once built, it won’t take any dev dependencies to the runner image, which makes it more lightweight and more secure.
podman build -t vite-app .
podman run -p 8080:8080 vite-app
Minecraft Server on a Raspberry Pi
This was something I had for my kids - a Minecraft Server on a Raspberry Pi 5. I used docker on Raspberry Pi 5, not podman.
First I had this command:
docker run -d --restart unless-stopped --name mcserver -e MEMORYSIZE='2G' -e PAPERMC_FLAGS='' -v /home/<MY-USER-NAME>/minecraft-server/mcserver-world-2024-01-27:/data:rw -p 25565:25565 -it marctv/minecraft-papermc-server:1.21.3-2
Whenever I needed to upgrade the server version, I could delete the container and spin up a new one from the most recent image:
docker rm mcserver
Docker Compose
The command is quite long, and even though it’s in .bash_history
, it’s way to long to deal with, so I translated it to a docker-compose.yml
file:
services:
mc:
restart: unless-stopped
container_name: mcserver
environment:
- MEMORYSIZE=2G
- PAPERMC_FLAGS=
volumes:
# relative path didn't really work
- /home/<MY-USER-NAME>/minecraft-server/mcserver-world-2024-01-27:/data:rw
ports:
- 25565:25565
stdin_open: true
tty: true
# tag :latest is outdated
image: marctv/minecraft-papermc-server:1.21.3-25
pull_policy: always
With that starting and stopping the minecraft container is really easy:
docker compose up -d
docker compose down
Dev Container and Github Codespaces
Github Codespaces is a way to code on the go. I created my first Dev Container
For Hugo blogs in particular you need to make sure you pay attention to some details.
- hugo version, I prefer being specific in the version, both for netlify.toml and devcontainer.json - to avoid bad surprises.
- hugo extended,
- git submodule, if you have a theme which you load as submodule, otherwise you’ll see “Page Not Found”.
Here is my simple .devcontainer/devcontainer.json
file:
{
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"ghcr.io/devcontainers/features/hugo:1": {
"extended": true,
"version": "0.147.0"
}
},
"forwardPorts": [1313],
"postCreateCommand": "git submodule update --init"
}
You can add more: vscode extensions, customizations, I found this blog post very useful:
To start the server run this command:
hugo server -D --appendPort=false --baseURL https://$CODESPACE_NAME-1313.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN
Even images work, I took a screenshot on my computer, selected the right folder in the codespaces and pasted the image and it worked.
Github Codespaces Billing is quite generous in included free hours.
Running Dev Containers locally
There is also a way to run a dev container locally on a computer. With the Dev Containers Extension you can start it directly from Visual Studio Code.
Install the extension:
code --install-extension ms-vscode-remote.remote-containers
Update the docker path to podman
.
start podman machine:
podman machine start
It worked fine for me. If you run into some roadblocks, I can recommend this blog post: