Docker has become the de facto standard for deploying applications in 2026. This guide walks you through setting up a complete Docker environment on Vultr — from initial server configuration to production-ready container orchestration with Docker Compose.
Why Run Docker on Vultr?
Vultr's high-frequency compute instances paired with all-SSD storage deliver exceptional I/O performance for container workloads. Whether you're running a single Node.js application or orchestrating a multi-service architecture, Vultr's $5/month plan provides enough resources to get started. Compare this to managed container services that can cost 5-10x more for equivalent resources.
Prerequisites
- A Vultr account (use our referral link for $100 free credit)
- A Vultr VPS running Ubuntu 22.04 LTS or 24.04 LTS
- SSH access to your instance
- Basic command line familiarity
Step 1: Initial Server Setup
First, ensure your system is up to date:
sudo apt update && sudo apt upgrade -y
Create a new sudo user (avoid using root for daily operations):
sudo adduser dockeradmin
sudo usermod -aG sudo dockeradmin
Set up SSH key-based authentication for enhanced security:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # Paste your public key
Step 2: Install Docker Engine
Docker provides an official installation script for Ubuntu. This is the fastest way to get started:
curl -fsSL https://get.docker.com | sh
This script installs Docker, creates the docker group, and starts the Docker daemon. Verify the installation:
docker --version
# Output: Docker version 27.x.x, build xxxxxx
sudo systemctl status docker
Add your user to the docker group to run Docker commands without sudo:
sudo usermod -aG docker dockeradmin
# Log out and back in for group membership to take effect
Step 3: Install Docker Compose
Docker Compose V2 is included with Docker Desktop but on Linux servers, you need to install it separately. For production use, download the latest stable release:
# Install Docker Compose v2
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Verify
docker compose version
# Output: Docker Compose version v2.x.x
Step 4: Configure Docker for Production
Production Docker setups require additional configuration for security and performance.
Configure Docker Daemon
Create or edit the Docker daemon configuration:
sudo nano /etc/docker/daemon.json
Add this production-optimized configuration:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"live-restore": true,
"default-address-pools": [
{"base": "172.17.0.0/12", "size": 24}
]
}
Restart Docker to apply changes:
sudo systemctl restart docker
Configure Firewall (UFW)
Only allow necessary ports. Docker exposes several ports by default — configure UFW to restrict access:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Only if you need direct Docker API access:
# sudo ufw allow 2375/tcp # NOT recommended without TLS
Step 5: Deploy Your First Application
Let's deploy a real-world application — a Nginx reverse proxy with a simple web app. Create the project structure:
mkdir -p ~/projects/webapp
cd ~/projects/webapp
nano docker-compose.yml
Add this Docker Compose configuration:
version: '3.8'
services:
web:
image: nginx:alpine
container_name: webapp_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- webapp_network
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
app:
image: node:20-alpine
container_name: webapp_api
working_dir: /app
command: npx http-server -p 3000
restart: unless-stopped
networks:
- webapp_network
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
networks:
webapp_network:
driver: bridge
Create the Nginx configuration:
mkdir -p ~/projects/webapp/html
nano ~/projects/webapp/nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /api {
proxy_pass http://app:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_bypass $http_upgrade;
}
}
}
Launch the application:
cd ~/projects/webapp
docker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -f
Step 6: Secure Your Docker Setup
Production containers need proper security hardening:
Use Non-Root Users in Containers
Modify your docker-compose.yml to run as non-root:
services:
web:
image: nginx:alpine
user: "101:101" # nginx user UID/GID
Limit Container Resources
services:
app:
image: node:20-alpine
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Enable Docker Content Trust
export DOCKER_CONTENT_TRUST=1
This prevents pulling unverified images from Docker Hub.
Step 7: Set Up Automated Backups
Container data persists in volumes. Set up a backup strategy using a simple cron job:
# Create backup script
sudo nano /usr/local/bin/docker-backup.sh
#!/bin/bash
BACKUP_DIR="/backup/docker"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup all volumes
for volume in $(docker volume ls -q); do
docker run --rm \
-v ${volume}:/volume \
-v ${BACKUP_DIR}:/backup \
alpine \
tar czf /backup/${volume}_${DATE}.tar.gz -C /volume .
done
# Keep only last 7 days
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
sudo chmod +x /usr/local/bin/docker-backup.sh
# Add to crontab
sudo crontab -e
0 2 * * * /usr/local/bin/docker-backup.sh >> /var/log/docker-backup.log 2>&1
Real-World Example: Deploying a Node.js API
Here's a practical example deploying an Express.js REST API with PostgreSQL:
# docker-compose.yml for Node.js + PostgreSQL
services:
api:
build: ./api
container_name: express_api
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
db:
condition: service_healthy
restart: unless-stopped
ports:
- "3000:3000"
db:
image: postgres:16-alpine
container_name: postgres_db
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
Build and deploy:
docker compose up -d --build
docker compose logs -f api
Monitoring Your Containers
Production deployments need monitoring. Add Portainer for a web-based Docker management interface:
docker volume create portainer_data
docker run -d \
--name portainer \
--restart unless-stopped \
-p 9000:9000 \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
Access Portainer at https://your-vultr-ip:9443. Note: Always secure this with strong passwords and consider restricting access via firewall.
Troubleshooting Common Issues
Docker Daemon Not Starting
sudo journalctl -u docker -f
sudo dockerd --debug # Run in foreground to see errors
Permission Denied Errors
Usually caused by incorrect volume permissions. Fix by adjusting host directory ownership:
sudo chown -R $(id -u):$(id -g) ./your_volume
Out of Disk Space
Docker images can consume significant space. Regular cleanup helps:
docker system prune -a
docker volume prune
Performance Tuning for Vultr
Vultr's SSD storage performs best with Docker when you optimize the storage driver. The overlay2 driver (default on Ubuntu) is already optimized, but ensure you're not using devicemapper on old kernels.
For high-throughput applications, consider Vultr's High Frequency compute instances — they deliver up to 3.8GHz CPU speeds, significantly improving container startup times and I/O performance.
Conclusion
A proper Docker setup on Vultr gives you a powerful, cost-effective container platform. Starting at $5/month, Vultr's VPS instances provide excellent value compared to managed container services. The key takeaways:
- Always use Docker Compose for multi-container applications
- Configure resource limits to prevent container runaway
- Implement regular backups of persistent volumes
- Use health checks for automatic container recovery
- Monitor with tools like Portainer or Prometheus
Ready to deploy? Get started with a free Vultr account and use our other tutorials to build your infrastructure.
For sports enthusiasts interested in combining tech with recreation, explore Cloudbet Guide for expert betting insights.
Start Your Docker Journey on Vultr
Get $100 free credit when you sign up via our link:
Deploy Docker on Vultr Now