Draft: review, adjust it to your own voice and add screenshots before publishing.
Where I started
This site began as static HTML on GitHub Pages and later Firebase Hosting. That was fine for a portfolio that never changed, but I wanted to write articles without editing HTML by hand, receive contact messages and understand who was visiting. So I turned it into a real application and used it as a lab for the DevOps practices I apply at work.
The application
The backend is Flask, split into blueprints: the public pages, the blog, analytics and an admin area. Articles are stored in SQLite through SQLAlchemy and written in Markdown in an editor with live preview, drafts and image uploads. For a personal site, SQLite is the right trade-off: a single file, trivial to back up, and no database server to maintain.
Pages are rendered on the server in Portuguese and English, with hreflang links between versions, so search engines show each audience the right language.
Containers and server
The app runs in Docker behind gunicorn. It shares a VPS with other projects of mine, such as BookCase, and all of them sit behind a single nginx that terminates HTTPS (Let's Encrypt certificates) and routes each domain to the right container over Docker's internal network.
The database, uploaded images and IP databases live on a volume outside the container, so every deployment rebuilds the image from scratch without losing data. A second container, with hard CPU and memory limits, runs background jobs without competing with the website.
Automated deployments
At first, deployment was a script I ran from my machine: SSH into the server, pull the code and rebuild the container. It worked, but it depended on me and never ran tests.
Now every push to main triggers a GitHub Actions pipeline:
- install dependencies and run the test suite (pytest, around a hundred tests);
- build the Docker image to make sure the build is not broken;
- if everything passes, connect to the VPS with a dedicated deploy key, update the code, rebuild the containers and reload nginx;
- check that the site answers HTTP 200.
If a test fails, nothing reaches production. The pipeline and the manual fallback share the same deployment script, so the two paths never drift apart.
Cookie-free analytics
I wanted to know how many people visit, where they come from and what they read, without Google Analytics and without a cookie banner. The answer was to collect on the server:
- Anonymous visitors: each visitor becomes a hash of IP and browser with a salt that rotates every day. Unique visitors per day can be counted without storing any IP address.
- Location and network: free DB-IP databases provide the approximate city and the organization that owns the IP. That also filters out data-center traffic, which is almost always bots.
- Clicks and reading: a small script records link clicks and, when the visitor leaves, how long they stayed and how far they scrolled.
It all feeds a dashboard with a map, a contact funnel and reading time per article.
What I learned
- Automate early. The pipeline took little time to set up and removed the fear of breaking the site on every deploy.
- Start simple. SQLite and a deployment script carried the project for a long time; complexity came in only when it was needed.
- Privacy and metrics are not opposites. You can measure what matters without tracking anyone.
Need something similar?
I help businesses and developers ship web apps to production: Docker, CI/CD, HTTPS, backups and monitoring. See how I can help.
Comentários
Nenhum comentário ainda. Seja o primeiro!
Deixar um comentário