← Back to the section

You started Postgres in a container, added some data, then removed the container — and everything was gone. This is not a bug, it is Docker's normal behavior. Let's figure out why it works this way and how to properly store data that must survive restarts.

Why data in a container is ephemeral

A container is an isolated process with its own file system. This file system is created from an image when the container starts, and when the container is removed it disappears along with it.

Short formula: an image is an immutable template, a container is a temporary working copy.

Everything you write inside a running container (rows in a database, uploaded files, logs) lives in the so-called writable layer — a thin layer on top of the image. Docker deletes this layer together with the container when you run docker rm.

docker run --name demo postgres:16
# ... created tables, added rows ...
docker rm demo
# The data is gone

For most cases this is actually convenient: you start a container for a test, play around, remove it — clean. But databases, user files, and application state need to live longer than a single container.

Three ways to store data outside a container

Docker offers three mechanisms: named volumes, bind mounts, and tmpfs. Each has its own use case.

A volume (named volume) is a directory managed by Docker. It lives on the host in a special place (/var/lib/docker/volumes/) and does not depend on the container's lifecycle.

# Create a volume explicitly
docker volume create pgdata

# Attach the volume to a container
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \  # volume:path_inside_container
  -e POSTGRES_PASSWORD=secret \
  postgres:16

Now you can remove the container, create a new one with the same volume — and the data will not go anywhere:

docker rm postgres
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16
# All data is still there

You manage volumes with docker volume:

docker volume ls                # list volumes
docker volume inspect pgdata    # where it lives, when it was created
docker volume rm pgdata         # remove (only if not attached)
docker volume prune             # remove all "orphaned" volumes

Named volumes are the right choice for databases, file stores, and any data you need in production.

Bind mounts — for development

A bind mount mounts a specific directory or file from the host straight into the container. No Docker "magic": you tell it yourself which path on your machine ends up where inside.

docker run -d \
  --name app \
  -v /Users/vva/projects/myapp:/app \  # host:container
  eclipse-temurin:21-jre \
  java -jar /app/app.jar

The main use case is development: you edit code in the IDE, and the changes are immediately visible inside the container without rebuilding the image. For Spring Boot this is especially handy together with devtools.

# docker-compose.yml for development
services:
  app:
    image: eclipse-temurin:21-jre
    volumes:
      - ./build/libs:/app          # the built jar from the host
    command: java -jar /app/app.jar

You should not use bind mounts to store Postgres data or other state in production: you become dependent on the file system structure of a specific host, and that breaks portability.

tmpfs — data in memory only

tmpfs mounts a directory inside the container into the host's RAM. The data never touches the disk and disappears when the container stops.

docker run --tmpfs /tmp myapp

It is used for temporary data that should not reach the disk: sensitive files (tokens, keys), session caches, intermediate computations. Not available on Windows hosts.

Comparison: when to use what

SituationMechanism
Database in production (Postgres, Redis)Named volume
Files uploaded by usersNamed volume
Code during local developmentBind mount
Configuration files during developmentBind mount
Temporary files, sensitive datatmpfs

Postgres in a container: a complete example

Let's run Postgres with a named volume so the data persists across restarts. This is a typical setup for local development of a Spring Boot application.

# docker-compose.yml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: secret
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data   # named volume

volumes:
  pgdata:   # Docker will create the volume automatically
docker compose up -d
# Created tables, added data...

docker compose down          # stopped and removed the containers
docker compose up -d         # brought them back up — the data is still there

Note: docker compose down does not remove volumes. To remove a volume you need an explicit flag:

docker compose down --volumes   # remove containers AND volumes

This protects you from accidental data loss.

Where to look if the data is gone

If the data disappeared after a container restart — check step by step:

  1. Make sure a volume was used, and not just a directory inside the container.
  2. Check that the --volumes flag was not used when stopping.
  3. Make sure the mount path matches where the application actually writes its data. For Postgres it is /var/lib/postgresql/data, for Redis it is /data, others have their own paths — check the image's documentation.
docker inspect postgres | grep -A 10 Mounts   # check what is mounted

In short

  • Data inside a container is ephemeral: remove the container — lose the data.
  • Named volume is the right choice for databases and any state that must live longer than the container.
  • Bind mount mounts a directory from the host; good for development, not for production.
  • tmpfs is memory only, disappears on stop; for temporary and sensitive data.
  • docker volume ls / inspect / rm / prune — the main commands for managing volumes.
  • docker compose down does not remove volumes — you need an explicit --volumes.
  • Running containers — docker run flags, run modes, managing containers.
  • Docker Compose — how to describe multi-container applications and define volumes in docker-compose.yml.
  • Networking — how containers communicate with each other and with the host.