Vultr Docker Setup 2026: Complete Guide to Containerize Your Applications

May 26, 2026 · Tutorial

Containers changed how we deploy software. No more "works on my machine" — your app, its dependencies, and its runtime ship together as a sealed unit that runs identically on your laptop, a colleague's machine, and a production server. Docker made this accessible. Vultr makes it cheap. Together, they're one of the fastest paths from code to running service.

This guide covers installing Docker on a Vultr VPS, configuring it for production use, setting up Docker Compose for multi-container projects, hardening the setup, and deploying a real web application. Everything works on a $5/month instance — no GPU required.

Why Docker on Vultr?

Traditional server management means fighting dependency conflicts. Your Node app needs v18, your Python script needs v3.11, your Ruby app needs 3.0. On a bare server, someone wins and everyone else适配 (adapts). Docker isolates each application in its own container with its own dependencies, so nothing conflicts.

Vultr's VPS instances are ideal for Docker because:

Prerequisites

You'll need:

Step 1: Install Docker Engine on Vultr

The fastest path is using Docker's convenience script. For production setups, the official APT repository gives you version control. Here's both:

Quick Install (Convenience Script)

# SSH into your Vultr VPS as your sudo user ssh user@your_vultr_ip # Download and run Docker's convenience script curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh # Executing docker install script, branch: main # Installation complete!

Production Install (Official APT Repository)

# Update package index and install prerequisites sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg lsb-release # Add Docker's official GPG key sudo mkdir -p /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # Set up the repository echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Install Docker Engine sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

The convenience script is fine for dev machines. The APT repository method gives you control over which Docker version you're running — critical for production stability.

Step 2: Configure Docker for Production Use

Manage Docker as a Non-Root User

By default, Docker requires root privileges. Add your user to the docker group to avoid sudoing every container command:

# Add your user to the docker group sudo usermod -aG docker $USER # Apply the new group membership (log out and back in, or run): newgrp docker # Verify it works — you should see your username and groups id | grep docker uid=1000(username) gid=1000(username) groups=1000(username),4(adm),24(cdrom),27(sudo),999(docker)

Configure Docker Daemon Options

For a production VPS, configure Docker's daemon with sensible defaults. Create or edit the daemon configuration file:

sudo nano /etc/docker/daemon.json

Add this configuration:

{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "storage-driver": "overlay2", "default-address-pools": [ { "base": "172.17.0.0/16", "size": 24 } ], "live-restore": true, "default-ulimits": { "nofile": { "Name": "nofile", "Hard": 65536, "Soft": 65536 } } }

This sets up structured logging with rotation (max 10MB per file, 3 files max), uses the overlay2 storage driver (fast and stable on Ubuntu), enables live restore so containers survive Docker restarts, and raises file descriptor limits for high-concurrency workloads.

# Restart Docker to apply changes sudo systemctl restart docker && sudo systemctl enable docker # Verify Docker is running docker info | head -20

Step 3: Deploy a Web Application with Docker Compose

Docker Compose manages multi-container applications. Let's deploy a real Python Flask application with a Redis cache layer — a common production pattern.

Project Structure

# Create project directory mkdir -p ~/flask-app && cd ~/flask-app # Create app directory structure mkdir -p app && mkdir -p nginx

Create the Flask Application

cat > app/app.py << 'EOF' from flask import Flask, jsonify from redis import Redis import os app = Flask(__name__) redis_host = os.environ.get('REDIS_HOST', 'redis') redis = Redis(host=redis_host, port=6379, decode_responses=True) @app.route('/') def home(): return jsonify({ 'message': 'Docker on Vultr is running smoothly', 'hits': redis.incr('page_hits'), 'version': '1.0' }) @app.route('/health') def health(): return jsonify({'status': 'healthy'}), 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000) EOF cat > app/requirements.txt << 'EOF' flask==3.0.* redis==5.0.* gunicorn==21.2.* EOF

Create the Dockerfile

cat > app/Dockerfile << 'EOF' FROM python:3.12-slim WORKDIR /app # Install dependencies before copying source COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application source COPY . . # Use gunicorn for production EXPOSE 5000 CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"] EOF

Create docker-compose.yml

cat > docker-compose.yml << 'EOF' services: web: build: ./app ports: - "5000:5000" environment: - REDIS_HOST=redis - FLASK_ENV=production depends_on: redis: condition: service_healthy restart: unless-stopped networks: - app-network redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 restart: unless-stopped networks: - app-network volumes: redis-data: networks: app-network: driver: bridge EOF

Build and Deploy

# Build and start the application docker compose up -d --build # Building web ... done # Containerizing... done # Starting flask-app-web-1 ... done # Starting flask-app-redis-1 ... done # Check running containers docker compose ps NAME IMAGE STATUS PORTS web-1 app-web running 0.0.0.0:5000->5000/tcp redis-1 redis:7 running 0.0.0.0:6379->6379/tcp # Test the application curl http://localhost:5000/ {"message":"Docker on Vultr is running smoothly","hits":1,"version":"1.0"} # Refresh to verify Redis is tracking hits curl http://localhost:5000/ {"message":"Docker on Vultr is running smoothly","hits":2,"version":"1.0"}

Step 4: Secure Your Docker Setup

Docker containers run with isolated namespaces by default, but a misconfigured deployment can expose your host. Here's how to harden:

1. Enable Docker Content Trust

Prevents pulling unsigned images:

export DOCKER_CONTENT_TRUST=1 # Add to ~/.bashrc to make it permanent echo 'export DOCKER_CONTENT_TRUST=1' >> ~/.bashrc

2. Limit Container Capabilities

Don't give containers more Linux capabilities than necessary:

# Run containers without NET_ADMIN capability (prevent network changes) docker run --cap-drop=NET_ADMIN myapp:latest # Run as non-root user inside the container docker run -u 1000 myapp:latest

3. Scan Images for Vulnerabilities

# Install Trivy (open source vulnerability scanner) sudo apt-get install -y trivy # Scan your Flask image trivy image flask-app-web:latest 2026-05-26 10:00:00 Scanning flask-app-web:latest... Total: 3 (UNKNOWN: 0, LOW: 2, MEDIUM: 1, HIGH: 0, CRITICAL: 0) LOW: CVE-2026-1 — python:3.12-slim base image, upgrade to 3.12.1 MEDIUM: CVE-2026-2 — flask: authentication bypass in < 3.0.1

4. Use a Firewall to Restrict Container Traffic

# Install UFW if not already present sudo apt install -y ufw # Allow SSH and web traffic only; block container-to-container exposure sudo ufw default deny incoming && sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable # Verify UFW status sudo ufw status verbose Status: active To Action From -- ------ ---- 22/tcp ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere

Step 5: Set Up Container Monitoring

Running containers is one thing; knowing when one is misbehaving is another. Here's a lightweight monitoring setup:

# Check container resource usage docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" NAME CPU % MEM USAGE web-1 0.23% 72.5MiB / 1GiB redis-1 0.11% 12.3MiB / 1GiB # View container logs (follow mode) docker compose logs -f web # Check disk usage of Docker docker system df TYPE TOTAL ACTIVE SIZE Images 2 2 142MB Containers 2 2 0B Local Volumes 1 1 0B

Step 6: Automate Cleanup

Containers accumulate. Old images, stopped containers, unused volumes — they eat disk space. Set up a cleanup routine:

# Clean unused images, stopped containers, and build cache docker system prune -f Total reclaimed space: 245MB # Add this to a cron job for automatic cleanup sudo crontab -e

Add this line to run weekly cleanup at 3 AM every Sunday:

0 3 * * 0 /usr/bin/docker system prune -f >> /var/log/docker-cleanup.log 2>&1

Deploy from GitHub Actions

Once your Vultr VPS is Docker-ready, you can automate deployments from GitHub. Here's the workflow:

cat > .github/workflows/deploy.yml << 'EOF' name: Deploy to Vultr on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and push uses: docker/build-push-action@v5 with: context: ./app push: true tags: ${{ secrets.DOCKER_USERNAME }}/flask-app:latest - name: Deploy on Vultr uses: appleboy/ssh-action@v1 with: host: ${{ secrets.VULTR_HOST }} username: ${{ secrets.VULTR_USER }} key: ${{ secrets.VULTR_SSH_KEY }} script: | cd ~/flask-app docker compose pull docker compose up -d EOF

Set your Vultr server IP, username, and SSH private key as GitHub secrets, and every push to main automatically deploys your latest image to your Vultr VPS.

Conclusion

Docker on Vultr is a production-grade combination: containers give you consistency and isolation, while Vultr's bare VPS gives you control and cost efficiency. The setup covered here — Docker Engine with proper daemon config, Docker Compose for multi-container apps, security hardening with capability limits and UFW, and automated cleanup — is production-ready out of the box.

Whether you're running a Flask app like this one, a Node.js API, a Python data pipeline, or a full microservices stack, Docker on Vultr handles it. Start with a $5/month instance, scale vertically when you need more resources, and deploy in minutes not hours.

Deploy a Docker-ready VPS on Vultr with $250 free credit — no annual commitment, cancel anytime.

🔗 Recommended Platforms

BC.GAME | Cloudbet

🎯 Recommended Betting Platforms

BC.GAME - Up to 300% Bonus Cloudbet - Best Crypto Sportsbook