How to Deploy Listmonk with Persistent Storage Using Docker on Any VPS
Learn how to deploy Listmonk on any VPS with Docker using persistent volumes. Step-by-step guide for AWS, GCP, Vultr setups.
If you're running email campaigns with Listmonk, losing subscriber data or campaign history is not an option. Yet many deployments fail because storage isn’t configured properly.
This guide shows exactly how to deploy Listmonk on any VPS (AWS, GCP, Vultr, or similar) using Docker — with persistent volumes so your data survives restarts, updates, and crashes.
The short answer: run Listmonk with Docker Compose, mount persistent volumes for both Postgres and uploads, and ensure proper networking and configuration. Once set up, your system becomes production-ready and stable.
Why Data Loss Happens in Listmonk Deployments
Most failed deployments happen because developers rely on default container storage. Containers are ephemeral by design — when they restart, data inside them disappears unless explicitly persisted.
According to Docker documentation, containers should be treated as stateless, with all critical data stored externally using volumes. Similarly, PostgreSQL recommends persistent storage for production databases to avoid data corruption or loss.
Here’s what typically goes wrong:
- Postgres runs without a mounted volume
- Uploads directory is not persisted
- Containers are recreated during updates
Once this happens, subscriber lists, campaign logs, and media files can vanish instantly.
The Solution — Persistent Listmonk Deployment with Docker
The correct approach is to separate your application and data using Docker volumes. This ensures:
- Database remains intact
- Media files are preserved
- System survives restarts and updates
Step 1: Prepare Your VPS
Works on any provider:
- AWS EC2
- GCP Compute Engine
- Vultr
- DigitalOcean
Minimum recommended specs:
- 2 GB RAM
- 1 vCPU
- Ubuntu 22.04
# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt updateTo install the latest version, run:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginStart Docker:
sudo systemctl enable docker
sudo systemctl start docker
Step 2: Create Production-Ready Docker Compose
Here is a clean, production-ready version of your configuration:
version: "3.8"
services:
app:
image: listmonk/listmonk:latest
container_name: listmonk_app
restart: unless-stopped
ports:
- "9000:9000"
depends_on:
- db
networks:
- listmonk
environment:
LISTMONK_app__address: 0.0.0.0:9000
LISTMONK_db__user: listmonk
LISTMONK_db__password: listmonk
LISTMONK_db__database: listmonk
LISTMONK_db__host: db
LISTMONK_db__port: 5432
LISTMONK_db__ssl_mode: disable
TZ: UTC
volumes:
- ./uploads:/listmonk/uploads
command: >
sh -c "
./listmonk --install --idempotent --yes &&
./listmonk --upgrade --yes &&
./listmonk
"
db:
image: postgres:17-alpine
container_name: listmonk_db
restart: unless-stopped
environment:
POSTGRES_USER: listmonk
POSTGRES_PASSWORD: listmonk
POSTGRES_DB: listmonk
volumes:
- listmonk_db_data:/var/lib/postgresql/data
networks:
- listmonk
networks:
listmonk:
volumes:
listmonk_db_data:
Step 3: Enable Persistent Volumes (CRITICAL)
This is what makes your setup production-ready.
Database persistence:
listmonk_db_data:/var/lib/postgresql/data
File persistence:
./uploads:/listmonk/uploads
These ensure:
- Subscriber data stays safe
- Campaign history is preserved
- Media files remain available
Step 4: Run the Deployment
Start your system:
docker compose up -d
Access Listmonk:
http://your-server-ip:9000
Create your admin account and login.
Step 5: Set Admin Credentials Automatically (Optional)
You can define admin credentials during first run:
LISTMONK_ADMIN_USER=admin
LISTMONK_ADMIN_PASSWORD=securepassword
This removes manual setup.
Why This Setup Works Better Than Default Installations
According to CNCF studies, containerized applications with persistent volumes reduce downtime risks by over 65%.
Here’s a comparison:
| Setup Type | Data Safety | Scalability | Reliability |
|---|---|---|---|
| Default Docker | Low | Medium | Low |
| Persistent Volumes | High | High | High |
| Kubernetes | Very High | Very High | Complex |
For most teams, Docker + volumes is the best balance.
Common Mistakes and How to Avoid Them
The most common mistake is exposing Postgres to the public internet. This creates a major security risk.
Another issue is forgetting to backup volumes. Even with persistence, backups are still essential.
We also see incorrect port bindings like:
127.0.0.19000:9000
This can break access completely.
Always:
- Bind ports correctly
- Keep database internal
- Backup your volumes
Real-World Example
At Hitori Tech, we deployed Listmonk for a high-volume email system handling over 500,000 subscribers.
Before persistent volumes:
- Data loss during updates
- Campaign interruptions
After implementing this setup:
- Zero data loss
- Stable deployments
- Faster recovery times
This is the difference between testing setup and production-grade infrastructure.
Frequently Asked Questions
How do I keep Listmonk data safe on Docker?
Use Docker volumes for Postgres and uploads. This ensures data is stored outside the container and survives restarts.
Can I deploy Listmonk on AWS or GCP?
Yes. Listmonk works on any VPS including AWS, GCP, Vultr, and DigitalOcean as long as Docker is installed.
Do I need a reverse proxy like Nginx?
Not required, but recommended for SSL and domain routing in production environments.
How do I back up Listmonk data?
Backup the Postgres volume and uploads folder regularly. You can use cron jobs or cloud backup tools.
Is Docker enough for production?
Yes, for most setups. Kubernetes is only needed for very large-scale deployments.
Running Listmonk with persistent storage turns it from a simple tool into a reliable email infrastructure system. If you're planning to scale campaigns or handle real users, this setup is not optional — it’s essential.
If you want help deploying or scaling systems like this, explore Hitori Tech services or reach out via Hitori Tech Contact.