AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 05 — Using Third-Party Images: Languages, Databases, and the Twelve-Factor App

05 — Using Third-Party Images: Languages, Databases, and the Twelve-Factor App

August 13, 20267 min read
Download as Markdown

"Just docker pull stuff" was my third-party-image strategy, and it conflated three uses that are nothing alike. The frame that separated them is about architecture, not tooling: a third-party image is a pre-configured process, and the design rules that make containerized apps behave well — stateless processes, externalized config, disposable lifecycles — are the Twelve-Factor App principles. [1][4]

The framing worth keeping is that "using third-party images" spans very different jobs. Pulling a language runtime to base my own image on, pulling a database to run as infrastructure, and pulling a one-shot CLI utility are three distinct patterns that happen to share one verb (pull) [1]. The skill is recognizing which one I'm doing, because each has a different design rule attached.

Three uses, three patterns

Language runtime base for my own image Database infrastructure with state CLI utility run once, exit node:20 (base) my-app code + deps FROM node:20 → build on top postgres:16 configured process volume (data lives here) state on a volume, not in the image some-tool:latest docker run --rm … does its job, --rm cleans up

The three columns have different lifecycles, and conflating them is what produced my early messes. Each one deserves a sentence.

Languages: the base image

A language image (node:20, python:3.12, golang:1.22) is a starting point I build on top of [2]. My Dockerfile's FROM node:20 pulls that image and stacks my app's layers above it. Knowing my language matters here, because the efficiency of the final image depends on choices only a language-native would make: multi-stage builds for Go (compile in a fat image, copy the binary into a scratch image), Alpine variants for Node/Python (smaller, but watch out for musl-vs-glibc native deps), layer ordering so npm ci re-runs only when package.json changes [2]. The roadmap's point about "knowing at least one language" is really about this — I cannot build a tight image without understanding my language's package model and runtime.

Databases: infrastructure with a volume

A database image (postgres:16, mysql:8, mongo:7) is different in kind. I don't build on top of it; I run it as infrastructure, and the only thing I have to get right is where the data lives [3]. The image itself is stateless and throwaway — Postgres comes up, finds a data directory, and serves. The data has to be on a volume attached at docker run time, otherwise the moment I recreate the container my entire database is gone.

docker run -d \
--name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16

The -v pgdata:/var/lib/postgresql/data line is the load-bearing part. It puts the data directory on a named volume Docker manages, so I can docker rm -f db and bring up a fresh postgres:16 container pointing at the same volume, and my tables are still there [3]. The image and the data are finally separated — which is the whole point of how containers handle state.

CLI utilities: run once, exit

The third use is the one I underused for a long time. Any CLI tool shipped as an image can be run ad-hoc without installing it on my host [1]:

docker run --rm -v "$PWD:/work" -w /work mikefarah/yq eval '.a.b' config.yml

The --rm removes the container the moment it exits, so the tool leaves no trace on my system. I get a clean, version-pinned toolchain per command without polluting my laptop. This is the pattern behind running formatters, linters, scanners, or migration tools from CI without a global install step.

Application architecture: the Twelve-Factor rules

Once I had the three uses straight, the design question became interesting: how do I structure an app so it actually benefits from containers? The roadmap points at microservices and container design patterns, and the cleanest distillation I found is the Twelve-Factor App methodology [4][5]. It's a list of principles written in 2011 — before Docker — that containerization made the default way to build:

  • One codebase, many deploys. The same image runs in dev, staging, prod.
  • Explicit, externalized configuration. Config lives in environment variables, never baked into the image. docker run -e DATABASE_URL=… is the Twelve-Factor config rule made literal.
  • Backing services treated as attached resources. A Postgres container, a managed RDS instance, and a third-party SaaS API are all "attached resources" — the app talks to them the same way, by URL.
  • Processes are stateless and share-nothing. Any state that needs to survive goes in a backing service (database, object store), never in the container's memory or writable layer [5].
  • Disposability. Containers start fast and shut down on SIGTERM cleanly, so the orchestrator can move them around.

The insight for me was that containers didn't invent these rules — they enforce them. A stateful container is a contradiction; a config-baked image can't be promoted between environments. The technology simply makes bad architecture painful fast, which is its real value.

How I use this

The payoff is a triage habit when I reach for an image. If I'm extending a runtime, I treat the image as a base and obsess over my own layers' size and cache order. If I'm running infrastructure, the first thing I wire up is the volume — the image is irrelevant if the data isn't persistent. If I'm running a one-shot tool, I reach for --rm and never think about it again. And when I design the app itself, I run the Twelve-Factor checklist: is config externalized, is state in a backing service, will it die cleanly on SIGTERM? Containers reward that design and punish its absence, which is why the rules finally feel less like opinion and more like physics.

References

[1] Docker, Inc., "Docker Hub Registry," hub.docker.com, 2024. [Online]. Available: https://hub.docker.com/

[2] Docker, Inc., "Building images — concepts," Docker Docs, 2024. [Online]. Available: https://docs.docker.com/get-started/docker-concepts/building-images/

[3] Docker, Inc., "Use case — Run a database in a container," Docker Docs, 2024. [Online]. Available: https://docs.docker.com/guides/use-case/databases/

[4] A. Wiggins, "The Twelve-Factor App," 12factor.net, 2017. [Online]. Available: https://12factor.net/

[5] microservices.io, "Microservices Architecture," microservices.io, 2024. [Online]. Available: https://microservices.io/

[6] Kubernetes, "Container Design Patterns," Kubernetes Blog, 2016. [Online]. Available: https://kubernetes.io/blog/2016/06/container-design-patterns/

Knowledge check · Question 1 of 5

When you `docker run postgres:16`, where must the data live for it to survive a container recreation?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!