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 upUpload 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 upRefresh 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 upUpload the image again and follow the link to confirm that it is available.
Now stop the application and remove the containers:
docker-compose rm -fThe container is gone, but the appdata volume remains. Start the application again:
docker-compose upRefresh 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.
Join the discussion!
Comments
Want to comment on this lesson?
Log in to leave comments.