Skip to content

setting up a VPS with Docker

Running apps in the cloud buys you reliability, reach, and easy scaling. This guide sets up a Virtual Private Server (VPS) with Docker and a simple Swarm stack, using environment variables so you can reuse the same workflow across projects.


Before you start, define a few environment variables locally:

Terminal window
# Server details
export SERVER_IP="your-server-ip"
export DOMAIN="your-domain.com"
export PROJECT_NAME="your-project-name"
# User and email you will use for deployment and notifications
export DEPLOY_USER="deploy"
export DEPLOY_EMAIL="${DEPLOY_USER}@${DOMAIN}"

SSH into the freshly provisioned server:

Terminal window
ssh root@${SERVER_IP}

Once you are in, install Docker and its dependencies:

Terminal window
sudo apt-get update
sudo apt-get 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
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io -y

At this point, docker ps should work on the server.


Create an A record pointing your domain to SERVER_IP. After DNS propagates, you can SSH using the domain instead of the raw IP:

Terminal window
ssh root@${DOMAIN}

To run Docker commands against the VPS from your local machine, create a remote Docker context:

Terminal window
docker context create ${PROJECT_NAME} --docker host=ssh://root@${DOMAIN}
docker context use ${PROJECT_NAME}

From now on, any docker ... command you run targets the VPS through this context.


Turn the VPS into a single-node Swarm manager:

Terminal window
docker swarm init # Optionally: --advertise-addr ${SERVER_IP}

Docker prints a join command with a token. Keep it somewhere safe if you plan to add worker nodes later:

Terminal window
# Example output, your token will differ
docker swarm join --token SWMTKN-1-xxxxxxxxxxxxxxxxxxxx ${SERVER_IP}:2377

With Swarm running, deploy your stack using your docker-compose.yml (Swarm treats it as a stack file):

Terminal window
docker stack deploy -c docker-compose.yml ${PROJECT_NAME}

If your compose file defines a service bound to port 80, your app should now respond at:

http://${DOMAIN}

To inspect logs from your main service:

Terminal window
docker service logs ${PROJECT_NAME}_app

Adjust the service name if your compose file uses something different.


You now have:

  • a VPS reachable through your own domain
  • Docker installed and accessible via a remote context
  • a Swarm stack deployed from docker-compose.yml

From here you can iterate: add more services, attach extra nodes to the Swarm, set up HTTPS with a reverse proxy (Traefik, Caddy, or Nginx), and lock down the box with a non-root deploy user, firewall rules, and backups.