Other people's images: databases, tools and runtimes
Run Postgres and Redis without installing them, use CLI tools that vanish when done, run code with any language version, and pick images you can trust.
By the end of this lesson you'll be able to
0 of 5 completeSomebody already packaged it
Need Postgres for a project, or a different Python version, or jq just once? Don't install it. Run the
image someone already built, tested and keeps updated:
Databases in a minute
$ docker run -d --name db -e POSTGRES_PASSWORD=devpass -p 127.0.0.1:5432:5432 postgres:17-alpine $ docker exec db pg_isready -h 127.0.0.1 -U postgres # ready yet? (over TCP: see below) 127.0.0.1:5432 - accepting connections $ docker exec db psql -h 127.0.0.1 -U postgres -c "create table orders(id serial primary key, item text); insert into orders(item) values ('book'),('pen'); select * from orders;" CREATE TABLE INSERT 0 2 id | item ----+------ 1 | book 2 | pen (2 rows)
docker run -d --name db -e POSTGRES_PASSWORD=devpass -p 127.0.0.1:5432:5432 postgres:17-alpinedockerbuild and run containersrunstart a new container-drun in the background--name dbname the container-e POSTGRES_PASSWORD=devpassset an environment variable inside-p 127.0.0.1:5432:5432publish a port, host:containerpostgres:17-alpinethe image to start
docker exec db pg_isready -h 127.0.0.1 -U postgresexecrun a command in a running containerdbthe containerpg_isready -h 127.0.0.1 -U postgresthe command to run inside it
docker exec db psql -h 127.0.0.1 -U postgres -c "create table orders(id serial primary key, item text);psql -h 127.0.0.1 -U postgres -c "create table orders(id serial primary key, item text);the command to run inside it
A database needs a moment to initialise, so scripts wait with pg_isready (or docker compose health
checks, lesson 9) instead of sleep. There's a trap here: on its first start, the official image runs a
temporary server, listening only on a local socket, to create the database, then restarts. A plain
pg_isready says "ready" during that temporary phase and the next command fails with connection to server on socket … failed. Checking over TCP with -h 127.0.0.1 waits for the real server, because the temporary
one never opens a TCP port. Forget the password and the container stops straight away, with the
reason in its logs:
$ docker run -d --name db2 postgres:17-alpine $ docker ps -a --filter name=db2 --format '{{.Names}} {{.Status}}' db2 Exited (1) 3 seconds ago $ docker logs db2 Error: Database is uninitialized and superuser password is not specified. You must specify POSTGRES_PASSWORD to a non-empty value for the superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
docker run -d --name db2 postgres:17-alpinedockerbuild and run containersrunstart a new container-drun in the background--name db2name the containerpostgres:17-alpinethe image to start
docker ps -a --filter name=db2 --format '{{.Names}} {{.Status}}'pslist running containers-ainclude stopped ones too--filter name=db2show only matching items--format '{{.Names}} {{.Status}}'print only these fields (Go template)
docker logs db2logsshow a container’s outputdb2the container
Redis is even quicker:
$ docker run -d --name cache -p 127.0.0.1:6379:6379 redis:8-alpine $ docker exec cache redis-cli ping PONG $ docker exec cache redis-cli set visits 41 && docker exec cache redis-cli incr visits OK 42
docker run -d --name cache -p 127.0.0.1:6379:6379 redis:8-alpinedockerbuild and run containersrunstart a new container-drun in the background--name cachename the container-p 127.0.0.1:6379:6379publish a port, host:containerredis:8-alpinethe image to start
docker exec cache redis-cli pingexecrun a command in a running containercachethe containerredis-cli pingthe command to run inside it
docker exec cache redis-cli set visits 41 && docker exec cache redis-cli incr visitsredis-cli set visits 41the command to run inside it&&run the next command only if this one workedredis-cli incr visitsthe command to run inside it
127.0.0.1: in -p keeps them reachable only from your own machine. These databases keep their data in the
container's writable layer, so docker rm deletes it; lesson 6 fixes that with volumes.
Tools that leave nothing behind
Run a tool once with --rm, feed it a file through standard input with -i, and nothing is left afterwards:
$ docker run --rm -i ghcr.io/jqlang/jq:1.8.1 '.orders[].item' < orders.json "book" "pen" $ docker run --rm curlimages/curl:8.16.0 -sI https://example.com | head -1 HTTP/2 200 $ docker run --rm --network host postgres:17-alpine \ psql "postgresql://postgres:devpass@127.0.0.1:5432/postgres" -c "select count(*) from orders;"
docker run --rm -i ghcr.io/jqlang/jq:1.8.1 '.orders[].item' < orders.jsondockerbuild and run containersrunstart a new container--rmdelete the container when it exits-ikeep standard input openghcr.io/jqlang/jq:1.8.1the image to start'.orders[].item'the command run inside, instead of the image’s default<read input from a fileorders.jsonthe command run inside, instead of the image’s default
docker run --rm curlimages/curl:8.16.0 -sI https://example.com | head -1curlimages/curl:8.16.0the image to starthttps://example.comthe command run inside, instead of the image’s default|pipe: send output to the next commandheadshow the first lines
docker run --rm --network host postgres:17-alpine \--network hostwhich network to joinpostgres:17-alpinethe image to start\the command run inside, instead of the image’s default
The last one borrows the Postgres image only for its psql client, so you get the exact client version that
matches the server. The images stay cached; next time they start instantly.
Any language version
Mount your folder into a runtime image and run your code with an exact version, without installing it:
$ docker run --rm -v "$PWD":/work -w /work python:3.13-slim python total.py total: 13.7 $ docker run --rm python:3.13-slim python --version Python 3.13.16 $ docker run --rm node:22-alpine node -e 'console.log(process.version)' v22.23.3
docker run --rm -v "$PWD":/work -w /work python:3.13-slim python total.pydockerbuild and run containersrunstart a new container--rmdelete the container when it exits-v "$PWD":/workmount a volume or folder, source:path-inside-w /workworking directory inside the containerpython:3.13-slimthe image to startpython total.pythe command run inside, instead of the image’s default
docker run --rm python:3.13-slim python --versionpython --versionthe command run inside, instead of the image’s default
docker run --rm node:22-alpine node -e 'console.log(process.version)'node:22-alpinethe image to startnode -e 'console.log(process.version)'the command run inside, instead of the image’s default
-v "$PWD":/work makes the current folder appear at /work inside, and -w /work starts the command there.
Need to check something against Python 3.12 as well? Change the tag. No conflicts with what's on your machine.
Choosing an image you can trust
| Look for | Why |
|---|---|
| Docker Official Image or Verified Publisher badge | maintained, rebuilt for security fixes, documented |
a pinned tag: postgres:17-alpine, not postgres | latest changes under you; a rebuild could jump a major version |
-slim or -alpine variants | smaller download and fewer packages with vulnerabilities |
| the image page's environment variables and volumes | how to configure it and where it keeps data |
| a linked source repository | you can see how it's built |
-alpine uses musl instead of glibc. That's fine for most images, but some compiled language packages behave
differently, so -slim (Debian, smaller) is the safer choice for Python and Node apps.
A disposable dev stack
Without installing anything but Docker, run Postgres and Redis for local development, bound to your machine only; create a table and some rows; count visits in Redis; query the database with a psql client from an image; and summarise a JSON file with jq and Python run from images.
A table with two rows, a Redis counter, and jq and Python output, then everything removedReveal one safe solution
$ docker run -d --name db -e POSTGRES_PASSWORD=devpass -p 127.0.0.1:5432:5432 postgres:17-alpine $ until docker exec db pg_isready -h 127.0.0.1 -U postgres >/dev/null; do sleep 1; done $ docker exec db psql -h 127.0.0.1 -U postgres -c "create table orders(id serial, item text); insert into orders(item) values ('book'),('pen');" $ docker run -d --name cache -p 127.0.0.1:6379:6379 redis:8-alpine $ docker exec cache redis-cli incr visits 1 $ printf '{"orders":[{"item":"book","price":12.5},{"item":"pen","price":1.2}]}\n' > orders.json $ printf 'import json\nd=json.load(open("orders.json"))\nprint("total:", sum(o["price"] for o in d["orders"]))\n' > total.py $ docker run --rm -i ghcr.io/jqlang/jq:1.8.1 '[.orders[].price] | add' < orders.json 13.7 $ docker run --rm -v "$PWD":/work -w /work python:3.13-slim python total.py total: 13.7 $ docker rm -f db cache
docker run -d --name db -e POSTGRES_PASSWORD=devpass -p 127.0.0.1:5432:5432 postgres:17-alpinedockerbuild and run containersrunstart a new container-drun in the background--name dbname the container-e POSTGRES_PASSWORD=devpassset an environment variable inside-p 127.0.0.1:5432:5432publish a port, host:containerpostgres:17-alpinethe image to start
until docker exec db pg_isready -h 127.0.0.1 -U postgres >/dev/null; do sleep 1; donedostart of what the loop runs each timedoneend of the loop
docker exec db psql -h 127.0.0.1 -U postgres -c "create table orders(id serial, item text); insert into orders(item) values ('book'),('pen');"execrun a command in a running containerdbthe containerpsql -h 127.0.0.1 -U postgres -c "create table orders(id serial, item text); insert into orders(item) values ('book'),('pen');"the command to run inside it
docker run -d --name cache -p 127.0.0.1:6379:6379 redis:8-alpine--name cachename the container-p 127.0.0.1:6379:6379publish a port, host:containerredis:8-alpinethe image to start
docker exec cache redis-cli incr visitscachethe containerredis-cli incr visitsthe command to run inside it
printf '{"orders":[{"item":"book","price":12.5},{"item":"pen","price":1.2}]}\n' > orders.json>write output to a file, replacing it
docker run --rm -i ghcr.io/jqlang/jq:1.8.1 '[.orders[].price] | add' < orders.json--rmdelete the container when it exits-ikeep standard input openghcr.io/jqlang/jq:1.8.1the image to start'[.orders[].price] | add'the command run inside, instead of the image’s default<read input from a fileorders.jsonthe command run inside, instead of the image’s default
docker run --rm -v "$PWD":/work -w /work python:3.13-slim python total.py-v "$PWD":/workmount a volume or folder, source:path-inside-w /workworking directory inside the containerpython:3.13-slimthe image to startpython total.pythe command run inside, instead of the image’s default
docker rm -f db cachermdelete a container-fstop it first if running
until … pg_isready -h 127.0.0.1 waits for the real server, not the temporary first-start one, without
guessing a sleep. When you're done, docker rm -f
removes both containers, and with them their data: next lesson, you'll keep it.
Try it yourself
0 of 6 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
You run docker run -d postgres:17-alpine and it exits immediately. Why?
Official reading for this lesson
Or get every quiz answer right and it completes itself.