We can deploy applications to VPS / Cloud Instances to avail it in the public internet. When deploying for production we need some standard practices to ensure application is easy to manage.
Some of the feature to consider are:
- Auto TLS
- Easy image building and deployment
- Rolling updates
- Rollback when needed
Preparing VPS for deployment
Setup a brand new VPS with SSH and Necessary settings example
- Install Docker on VPS
- ssh into VPS using
ssh vps1.example.com
- install docker
- ssh into VPS using
curl -fsSL https://get.docker.com | sudo sh
Setting up Docker Context
We can use docker context to configure docker client to connect to the VPS. We can create a context using:
docker context create vps1 --docker "host=ssh://vps1.example.com"
Note: This setup needs SSH keys and DNS entries.
Preparing docker compose with Traefik
I’d like to deploy Traefik as reverse proxy and auto TLS for my applications.
Along with traefik, Beszel is a great lightweight monitoring tool to view system metrics.
The docker compose file looks like this:
Note: generate password with
htpasswd -nb admin password
and replace the line- "traefik.http.middlewares.auth-middleware.basicauth.users=admin:password"
services:
traefik:
image: traefik:3
labels:
- "traefik.enable=true"
# Dashboard router configuration
- "traefik.http.routers.dashboard.rule=Host(`vps1.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# Basic auth middleware
- "traefik.http.middlewares.auth-middleware.basicauth.users=admin:password" #
- "traefik.http.routers.dashboard.middlewares=auth-middleware"
command:
- "--providers.docker"
- "--providers.docker.exposedbydefault=false"
# TLS
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=adharsh.knullsoft@gmail.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# WebGateway
- "--entrypoints.web.address=:80"
- "--entryPoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
# Dashboard configuration
- "--api.dashboard=true"
- "--api.insecure=false"
# Metrics
- "--metrics.prometheus=true"
- "--entryPoints.metrics.address=:8099"
- "--metrics.prometheus.entryPoint=metrics"
- "--metrics.prometheus.addEntryPointsLabels=true"
- "--metrics.prometheus.addServicesLabels=true"
ports:
- mode: host
protocol: tcp
published: 80
target: 80
- mode: host
protocol: tcp
published: 443
target: 443
volumes:
- letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
gateway:
deploy:
placement:
constraints:
- node.role == manager
beszel:
# default port is 8090
image: henrygd/beszel:latest
labels:
- "traefik.enable=true"
- "traefik.http.services.beszel.loadbalancer.server.port=8090"
# https router configuration
- "traefik.http.routers.beszel.entrypoints=websecure"
- "traefik.http.routers.beszel.rule=Host(`beszel-vps1.example.com`)"
- "traefik.http.routers.beszel.service=beszel"
- "traefik.http.routers.beszel.tls.certresolver=letsencrypt"
volumes:
- beszel_data:/beszel_data
networks:
- gateway
deploy:
placement:
constraints:
- node.role == manager
volumes:
beszel_data:
letsencrypt:
networks:
gateway:
Note: once beszel is deployed we can access it using
https://beszel-vps1.example.com
and continue setup for agent beszel docs
Deploy the stack
docker context use vps1
docker stack deploy -c docker-compose.yml vps1
Building images in VPS itself
Building image with docker context will allow images to be built and available in the VPS directly without Registry.
But It’s not recommended to build images on VPS as it will take more time and resources.
docker context use vps1
docker build -t myapp:latest .
Summary
With this setup a VPS is ready to accept more applications using docker stack.
new apps can use the following template:
services:
nginx:
# default port is 8090
image: nginx:latest
labels:
- "traefik.enable=true"
- "traefik.http.services.myapp.loadbalancer.server.port=8090"
# https router configuration
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.rule=Host(`app-vps1.example.com`)"
- "traefik.http.routers.myapp.service=myapp"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
networks:
- gateway
networks:
gateway:
extrenal: true # Connect to same network as traefik
This setup covers:
- Auto TLS with Traefik
- Easy image building and deployment (
docker build -t myapp:latest .
) - Rolling updates (
docker stack deploy -c new_docker-compose.yml vps1
) - Rollback on failure (
docker service update --rollback service_name
)
We could run all docker commands in the VPS remotely from our local machine using docker context use vps1
It’s a great stack to deploy small apps without complicated orchestration.