Video Lesson: Run stateless containers in Docker

Run stateless containers in Docker

Learn how to persist data beyond container lifecycles by using volumes with stateless Docker containers.


Lesson Content

Docker containers should be stateless. They need to survive system reboots and container termination without losing data. Stateless containers also make horizontal scaling easier because any container can be stopped and replaced at any time.

This example uses a small Express application that uploads files to the local file system. The base route serves an HTML form with a file input, and the upload route moves the uploaded file into an uploads directory.

The Dockerfile creates the working directory, copies the application files, creates the upload directory, installs the dependencies with yarn, exposes port 8080, and starts the web server.

The application runs with Docker Compose. The app service builds the image from the local Dockerfile and maps port 8080 in the container to port 8080 on the host:

version: '3'

services:
  app:
    build: .
    ports:
      - "8080:8080"

Start the application with:

docker-compose up

Upload an image through the form and follow the resulting link. The file appears in the container's uploads directory.

Now stop the application, remove its containers, and start it again:

docker-compose rm -f
docker-compose up

Refresh the browser and check the uploaded file. It is gone.

This happens because a container's file system is ephemeral. When the container is removed, any data written inside it is removed as well. If the application loses uploaded content whenever a container is replaced, it cannot safely scale or recover from failures.

The easiest way to persist this data is with a volume. A volume maps storage outside the container to a directory inside the container. When the container is removed, the volume remains available and can be mounted into a replacement container.

Open docker-compose.yml and add a top-level volumes property. Give the volume an arbitrary name, such as appdata:

version: '3'

services:
  app:
    build: .
    ports:
      - "8080:8080"

volumes:
  appdata:

Next, add a volumes property to the app service. Each volume is a list entry containing the volume name and the directory to mount inside the container. The application writes uploads to /srv/uploads, so mount appdata at that path:

version: '3'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - appdata:/srv/uploads
volumes:
  appdata:

The appdata volume now persists the contents of /srv/uploads independently of the container lifecycle.

Remove the existing containers so the application starts from a clean state, then start it again:

docker-compose rm -f
docker-compose up

Upload the image again and follow the link to confirm that it is available.

Now stop the application and remove the containers:

docker-compose rm -f

The container is gone, but the appdata volume remains. Start the application again:

docker-compose up

Refresh the browser. The uploaded file is still available because it was stored in the persistent volume rather than only in the container's ephemeral file system.

Volumes allow containers to be replaced, restarted, or scaled without losing application data. This is an essential part of running stateless containers: the container itself can be disposable while persistent data lives outside it.

Maggie

Hey, I'm Maggie! 👋

Ask me anything.

Want to chat with Maggie?

Log in for AI tutor access.

Comments

Join the discussion!

Notes

Keep track of what you learn!