Setting Up a VPS Server with Docker, Nginx Proxy Manager, and Portainer
Hosting apps on a VPS gets much easier when infrastructure is containerized from day one. With Docker for runtime, Nginx Proxy Manager for reverse proxy + SSL, and Portainer for container management, you get a clean and scalable baseline.
This guide uses a Debian-based server and walks through the full setup step by step.
Why It Matters
- Keeps deployment predictable with containers.
- Makes domain routing and SSL simpler with Nginx Proxy Manager.
- Gives operational visibility through Portainer UI.
- Builds a reusable foundation for multiple apps on one VPS.
Core Concepts
1. Install Docker on Debian VPS
Update packages and install Docker using official repository.
sudo apt update && sudo apt upgrade -y
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do
sudo apt-get remove -y $pkg
done
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Configure Docker for non-root usage:
sudo groupadd docker || true
sudo usermod -aG docker $USER
docker --version
2. Create Shared Docker Network
Create one external network for reverse-proxied services.
docker network create nginx_proxy
3. Deploy Nginx Proxy Manager
Create NPM stack and run it with Docker Compose.
mkdir -p ~/managers/nginx
cd ~/managers/nginx
services:
nginx-proxy-manager:
container_name: nginx
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
default:
name: nginx_proxy
external: true
docker compose up -d
Default login URL:
http://<your-vps-ip>:81
Default credentials:
- Email:
admin@example.com - Password:
changeme
Change these immediately after first login.
4. Deploy Portainer
Create Portainer stack:
mkdir -p ~/managers/portainer
cd ~/managers/portainer
services:
portainer:
container_name: portainer
image: portainer/portainer-ce:latest
restart: unless-stopped
ports:
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer_data:/data
networks:
default:
name: nginx_proxy
external: true
docker compose up -d
Portainer initial URL:
http://<your-vps-ip>:9000
5. Expose NPM via Domain and SSL
In NPM UI, create proxy host for NPM dashboard itself:
- Domain:
npm.example.com - Forward Hostname/IP:
nginx - Forward Port:
81 - Enable SSL + Force SSL + HTTP/2
- Request Let’s Encrypt certificate
6. Expose Portainer via Domain and SSL
Create second proxy host for Portainer:
- Domain:
portainer.example.com - Forward Hostname/IP:
portainer - Forward Port:
9000 - Enable SSL + Force SSL + HTTP/2
After validation, you can close direct admin ports and keep only 443 exposed publicly.
Practical Example
After both proxy hosts are configured:
https://npm.example.com-> Nginx Proxy Manager UIhttps://portainer.example.com-> Portainer UI
Now new services can be added behind NPM in minutes, not in three-terminal-debug mode.
Common Mistakes
- Using Docker from distro packages instead of official repo.
- Keeping default NPM admin credentials.
- Exposing management ports publicly forever.
- Not using a shared Docker network for proxy routing.
- Skipping persistent volume mounts for NPM/Portainer data.
Quick Recap
- Install Docker from official source.
- Create shared
nginx_proxynetwork. - Deploy NPM and Portainer as separate stacks.
- Configure domain-based SSL reverse proxy entries.
- Restrict insecure management access after setup.
Next Steps
- Add UFW hardening rules and fail2ban.
- Add automated backup for NPM and Portainer volumes.
- Add monitoring stack for server and container health.
- Add CI/CD deployment pipeline for app containers.