3rd migration complete ✌ . New era, new Docker stack
After 12 year I'm still here and, finally I did it, the 3rd big self hosted migration✌.
Nope, my VPS isn't so busy. Just generated some traffic to make it look cooler.
When I started the word "blog" didn't exist yet and internet was a mix of forums and websites, now it's full of social, videos and many people stopped to self host "things" because they prefere something that just works™ that they don't have to mantain. The funny fact it's has never been years to self host a web software as today!
Why?
Linux, DNS, reverse proxy, HTTPS, PHP, NodeJS, CMS, VPN, ssh, to self host even a stupid kitten blog, you have to wire a lot of tech, most technologies are open source but you have to know what you are doing and it's your responsibility to check that they continue talk fine even after upgrades. ..And it's not easy to follow any possible stack permutation!
OK, I've a landing page, how a deploy can be easier?
Let's say you have a new web page in ./app/index.html
and you want to publish it on on the Internet.. to https://make-self-hosting-great-again.grigio.org
what do you do?
It can be easy as using a docker-compose.yml
file and run a command!
# docker-compose.yml
version: "3"
services:
app:
image: busybox
restart: unless-stopped
volumes:
- ./app:/var/www
networks:
- web
labels:
- traefik.docker.network=web
- traefik.frontend.rule=Host:make-self-hosting-great-again.grigio.org
- traefik.port=9000
- traefik.backend=msgh
entrypoint: busybox httpd -f -p 0.0.0.0:9000 -h /var/www
networks:
web:
external: true
And then
docker-compose up -d
And we are live! https://make-self-hosting-great-again.grigio.org
Notes
-
Check with
dig subdomain.yourdomain.com
that the DNS propagation is complete, it could take some minutes. You should see the name resolved your IP. WithA * YOUR.VPS.IP
You can resolve all subdomains. -
Sometimes the HTTPS / Traefik / Letsencrypt certificate is not valid, you have to wait or try an anonymous session in the browser.. because it isn't updated on every request, at least on Chrome.
-
The network
web
is used by Traefik to automagically generate the HTTPS certificate, you could also use a network to share a database name resolution among multiple containers.
Making things simple is hard
To be honest, I tried several times in the past to do this migration, and I failed. At the time docker was immature to my needs but now it's another story. Here is a list of things of my pains:
-
Ideally docker containers are stateless, but some projects, specially old CMS mix app, config, plugins, web server extensions, database configs (ex. mod_rewrite for clean urls,..). There isn't a perfect solution, you can always mount that blob and use the container to freeze the stack.
-
Projects with not clear dependencies are pain. I had problems with old versions of Drupal 4 /6 and Meteor 1.0.3.x Stacks evolves and also Databases and you have to replicate the exact version to port them.
-
mysqldump
is a pain, it did a backup of a database with a corrupt table without errors.. but the dump ignored halt of the tables -_-
Thanks to the open source and docker ecosystem
Here some docker images I use:
Base infrastructure
- Reverse Proxy: Traefik
traefik:alpine
It is the only gateway to manage websites traffic and HTTPS certs renewal (port 80/443) - Container management web UI: Portainer
portainer/portainer
To have a visual feedback ofdocker
anddocker-compose
commands - System performance overview: Netdata
netdata/netdata
Extra infrastructure
- Email aliases
martinpesek/postfix-forwarding
- VPN server
kylemanna/openvpn
App infrastructure
- Databases:
mysql:5.7
- Stacks:
php:5-fpm
,ghost:0
- .. and more but with weird customizations
Conclusion
Some beautyful oneliners..
It creates a temporary mysql container to extract a database backup
docker run --net web -v $PWD/backup:/backup --rm -i mysql:5.7 sh -c "MYSQL_PWD=yourpassword mysqldump -h mysqlinstance -u yourdbuser yourdatabase | gzip -9 > /backup/dbbackup.sql.gz"
It attaches to mysql container to restore a database backup
docker exec -i mysqlinstance /usr/bin/mysql -u root --password=yourrootpassword -e "create database yourdatabase"
gunzip < backup/dbbackup.sql.gz | docker exec -i mysqlinstance /usr/bin/mysql -u yourdbuser --password=yourpassword yourdatabase
I hope you like it, the cloud isn't just Amazon AWS and Google.
Enjoy.