I like to test technologies and have many websites and other projects running. I used to have Windows VPS (still do, but currently migrating) and although updating websites was easy using Visual Studio's "Publish" functionality there were still some manual work like installing new versions of .NET or setting up SSL certificates for new websites. Paying for a Windows Server license wasn't nice either as it almost doubled the price of a cheap VPS.
So I decided to put my applications to containers and set up a Linux VM which could fulfill all these requirements
It turned out to be suprisingly easy and I ended up using these tools
Docker Engine is an application for running Docker containers.
Just follow official installation instructions https://docs.docker.com/engine/install/
For Ubuntu
Uninstall old versions of Docker
sudo apt-get remove docker docker-engine docker.io containerd runc
Update package repository
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
Set up repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.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 docker-ce docker-ce-cli containerd.io
Verify installation
sudo docker run hello-world
Docker Compose is a tool for running multiple containers as a unit.
Again, follow official instructions https://docs.docker.com/compose/install/
For Ubuntu
Download
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Apply executable permissions
sudo chmod +x /usr/local/bin/docker-compose
Verify installation
docker-compose --version
Portainer is container management application.
Traefik is a reverse proxy and load balancer. It handles mapping domains to containers and their SSL certificates.
There's official guide https://documentation.portainer.io/v2.0/ad/traefik/rp-traefik/
Copy the docker-compose.yml template and modify with your information. I changed the router name to portainer (frontend in the official template).
I also deleted the edge configuration as I don't need to manage many servers at the moment. Here's how my configuration looks (replace email and host).
version: "3.3"
services:
traefik:
container_name: traefik
image: "traefik:latest"
command:
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker
- --log.level=ERROR
- --certificatesresolvers.leresolver.acme.httpchallenge=true
- --certificatesresolvers.leresolver.acme.email=<YOUR_EMAIL>
- --certificatesresolvers.leresolver.acme.storage=./acme.json
- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/acme.json"
labels:
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
portainer:
image: portainer/portainer-ce:2.0.0
command: -H unix:///var/run/docker.sock
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
labels:
# Frontend
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`<YOUR_PORTAINER_HOST>`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
- "traefik.http.routers.portainer.service=portainer"
- "traefik.http.routers.portainer.tls.certresolver=leresolver"
volumes:
portainer_data:
Create acme.json (stores SSL certificate information) file next to docker-compose.yml
touch acme.json
Now you can start the containers
docker-compose up -d
Add DNS record if not yet in place.
Navigate to the Portainer dashboard (<YOUR_PORTAINER_HOST> in the docker-compose.yml).
Create admin account.
Portainer app templates have some common services like databases which are easy to set up.
First of all your apps must be in containers and available as packages from github.
Go to settings (user settings in the top right corner, not repository settings)
Developer settings > Personal access tokens > Generate new token
Give it a name and scope read:packages
Registries > Add registry > Custom registry
Registry URL: docker.pkg.github.com/<GITHUB_USERNAME>
Username: <GITHUB_USERNAME>
Password: <GITHUB_PERSONAL_ACCESS_TOKEN>
For some reason I had to pull the image (at least the first one from Github) before it could be used to create a container.
So open the Images tab and select the registry you just created and give the image name.
Now a new container can be created from the image.
Containers > Add container
If using Traefik for routing, insert labels.
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
Watchtower can update docker images automatically.
Containers > Create container
Registry: Dockerhub
Image: containrrr/watchtower:latest
Env:
Follow https://containrrr.dev/watchtower/private-registries/ to set up access to private registry access.
E.g. for github, first create auth string
echo -n '<GITHUB_USERNAME>:<GITHUB_PERSONAL_ACCESS_TOKEN>' | base64
Then create a json config file with your username and secret (mine is /srv/management/config.json next to other config files docker-compose.yml and acme.json)
{
"auths": {
"https://docker.pkg.github.com/<GITHUB_USERNAME>": {
"auth": "<SECRET>"
}
}
}
Last mount that file to watchtower container
Advanced container settings > Volumes
Container: /config.json
Type: bind
Host: <PATH_TO_CONFIG_FILE>
If you put WATCHTOWER_LABEL_ENABLE: true to watchtower env then you can mark containers with label com.centurylinklabs.watchtower.enable: true to update them automatically.
Put containers in the same network as Traefik if you want them to be accessible from the internet
Make sure Traefik router/service names are unique