In this walkthrough, I set up a VS Code Dev Container, beginning with my use of Remote-SSH on homelab LXCs and my goal of running AI coding agents like Claude Code in an isolated, easily recreated environment. I cover writing a Dockerfile and devcontainer.json, how the extension automatically handles the VS Code server and workspace bind mounts, and the difference between UI and workspace extensions, including declaring them in the config for reproducible setups. I leave handling SSH keys for pushing code to a future post...
When my home internet connection can’t reach DockerHub, I work around it by pulling the Jellyfin image I need on a laptop tethered to a phone hotspot, then moving it to the server as a file using docker save and docker load. The post also includes a short explanation of how container images are built from layers described by a manifest...
After choosing the tiny BusyBox httpd server for containerised websites, I encountered third-party benchmarks suggesting NGINX was dramatically faster. Running my own A/B tests with Apache’s ab tool initially gave misleading results, which turned out to be NGINX Proxy Manager serving cached responses. With cache-busting requests, BusyBox and NGINX performed similarly over the network, so I am sticking with the 1.35MB BusyBox containers over the roughly 49MB NGINX-alpine images...
How I make one Docker Compose service wait for another to be ready, using a healthcheck on the depended-on service and a depends_on condition with service_healthy on the dependent one. The worked example adds InfluxDB to my homelab monitoring stack so a Go metrics collector starts only after the database is up, with sample compose YAML included...
A BusyBox container serving a static site can’t fetch content over HTTPS because BusyBox ships without root CA certificates and has no package manager to install them. Rather than switch to a larger Alpine image, I settle on bind-mounting the host’s certificate directory into the container read-only, with example configurations for both docker-compose and docker run...
A follow-up on bundling static sites into BusyBox Docker containers, covering how to handle minor dynamic tasks such as periodically downloading an image into the directory of hosted static files. Rather than using cron, which behaves unexpectedly on BusyBox, my solution is a shell script with a download-and-sleep loop that runs in the background alongside the httpd server. I also note that BusyBox’s wget has TLS problems, a topic I defer to a later post...
Serving static websites from small Docker containers built on BusyBox httpd, coming in at around 4MB each. I walk through the Dockerfile setup, cross-platform builds from an M1 Mac to linux/amd64, pushing to the GitHub Container Registry, and deploying via docker-compose behind Nginx Proxy Manager, including DNS and SSL configuration...
I explain my move from Docker Hub to GitHub’s Container Registry for container images running on my VPSs, motivated by Docker Hub’s single-private-image free tier and the eventual possibility of CI/CD rebuilds. It walks through generating a Personal Access Token, logging in to ghcr.io, and pushing and pulling images with the registry included in the container name...
Running NGINX Proxy Manager in a Docker container breaks the usual habit of pointing proxies at 127.0.0.1, since localhost now refers to inside the container rather than the host. I explain why, cover the confusing exception of NPM’s own admin interface, and show how joining service containers to NPM’s Docker network lets the proxy reach them by container name via DNS, with no ports exposed to the host...
I explain why I use Docker bind mounts instead of named Docker volumes in my homelab, keeping each container’s compose file and data together in a single directory for easier backups and migration. I weigh the tradeoffs of this approach and walk through migrating an Uptime Kuma container from a named volume to a bind mount by locating the volume’s files with docker inspect and copying them directly while the container is stopped, contrary to more complicated methods suggested elsewhere online...
Having downloaded a pile of large language models to run locally with Ollama on my M1 MacBook, I compare how several of them (codeqwen, deepseek-coder, phi3, dolphin-mistral, and llama3 at different quantisation levels) answer a Docker question about CMD versus ENTRYPOINT. I time each response and judge it subjectively, with caveats that the test is unscientific and benchmark charts usually reflect full-size models on expensive hardware. My writeup includes a layman’s explanation of quantisation, brief notes on each model’s character, and I conclude that llama3 and most others gave usable answers while phi3 fell short...
I offer a short explainer on the difference between ENTRYPOINT and CMD at the end of a Dockerfile, with examples showing that command-line arguments replace CMD entirely but are appended to ENTRYPOINT. I also cover combining the two, and note that both execute at container launch, unlike RUN, which happens during the image build...
Docker environment variables set for a container aren’t available to scripts run by cron inside it, even when cron runs as the same user. After confirming the cause and finding no clean fix, I settle on a workaround: saving the needed variable to a file in the entry point script and reading it back from the cron job...
How to get console output from a cron job running inside a Docker container. I cover running cron in the foreground so the container doesn’t exit, and redirecting job output to /proc/1/fd/1 so it appears in the container’s stdout, since cron normally sends output to mail. I mention common gotchas like user permissions and file paths, with an example project on GitHub...
Upgrading Forgejo from 1.21 to 7.0.0 takes more than the usual pull on an existing tag, because the container tag changes with the minor version. After explaining how container image tags work, I update my docker-compose.yml to pin Forgejo 7.0 and run the usual backup, down, pull, up, and test steps...
A first-draft Dockerfile for my Node project, and how I keep the resulting image tidy by inspecting its contents with an interactive bash shell and adding entries to .dockerignore. I also explain my preference for copying everything over explicitly listing files, citing debugging time and security...
My VPS runs NGINX Proxy Manager, which offers no way to serve static virtual hosts, so I walk through running Static Web Server—a lightweight Rust-based web server—in Docker alongside it. I cover the directory layout, a docker-compose setup, and the config.toml virtual host entries, with the result verified using Tailscale addresses as stand-in hostnames...
When setting up self-hosted LimeSurvey, I find no official Docker image, so I work through how to decide which third-party container image to trust. I lay out rough criteria, such as known maintainers, popularity, update frequency, and inspectable build files, then compare several LimeSurvey images on Docker Hub and trace their provenance before settling on one...
I walk through turning my small Node.js Markdown server into a Docker image, explaining the Dockerfile line by line, building the image locally, and pushing it to Docker Hub. I finish with a sample docker-compose file so self-hosters can deploy the app with a single command...
I walk through hosting a private Docker registry on my homelab network using Docker’s official registry image, motivated by the free tier’s single-repository limit and Docker Hub’s pull/push rate limits. I cover setting up the registry with docker-compose, building a small test image, and pushing it to the registry without TLS by adding an insecure-registries exception to daemon.json. A third machine pulls and runs the image to confirm it all works...