Personal portfolio built with Astro, TypeScript and Docker, deployed to a self-hosted Linux VPS through a GitHub Actions CI/CD pipeline.
The project focuses on containerization, reverse proxying, automated deployments, networking and production infrastructure.
π caixeta.dev
The application runs on a self-hosted Linux VPS behind Cloudflare, with Traefik handling reverse proxying and TLS, and Nginx serving the generated Astro application.
Internet
β
βΌ
ββββββββββββββββ
β Cloudflare β
β β
β DNS / Proxy β
β Bot Defense β
β DDoS β
ββββββββ¬ββββββββ
β
β HTTPS
βΌ
βββββββββββββββββββββ VPS βββββββββββββββββββββ
β β
β Traefik β
β Reverse Proxy / TLS β
β β β
β βΌ β
β Nginx β
β β β
β βΌ β
β Astro Static Site β
β β
βββββββββββββββββββββββββββββββββββββββββββββββ
Cloudflare is used as the public edge layer, keeping the VPS origin IP hidden from normal visitors and providing an additional security layer in front of the infrastructure.
Traffic is routed through Cloudflare before reaching Traefik on the VPS.
This provides:
- Origin IP protection through Cloudflare's proxy
- DDoS protection
- Bot protection and filtering
- DNS management
- HTTPS/TLS at the edge
- Reverse proxying through Traefik
- A separation between the public edge and the application infrastructure
The production environment is composed of:
- Cloudflare β DNS, proxy, edge security and bot protection
- Linux VPS β self-hosted production environment
- Docker & Docker Compose β application packaging and orchestration
- Traefik β reverse proxy, routing and TLS
- Nginx β static file serving, compression and caching
- Let's Encrypt β TLS certificates
- GitHub Container Registry β Docker image registry
The application uses a multi-stage Docker build:
Node.js
β
βββ Install dependencies
βββ Build Astro application
β
βΌ
/dist
β
βΌ
Nginx Alpine
β
βΌ
Production Container
Node.js and the build tooling are not included in the final runtime image.
The deployment pipeline is fully automated with GitHub Actions.
Pull Request
β
βββ Prettier check
βββ Astro production build
Push to main
β
βΌ
GitHub Actions
β
βββ Run CI checks
βββ Build Docker image
βββ Push image to GHCR
βββ Trigger authenticated webhook
β
βΌ
VPS
β
Docker Compose
β
βΌ
New application
Images are tagged with the Git commit SHA, allowing each production deployment to reference an immutable version.
The deployment webhook is protected with HMAC-SHA256 authentication, so only authorized requests can trigger a deployment.
The portfolio also includes a technical blog built with MDX and Astro Content Collections.
Posts are version-controlled alongside the application:
src/posts/
βββ lsof-command-tutorial.mdx
βββ how-inode-works.mdx
βββ ssh-config.mdx
βββ ...
Content is statically generated during the Astro build, so the production environment does not require a database or application server.
This gives the blog a simple Git-based publishing workflow:
MDX β Git β CI β Build β Docker β Production
| Area | Technologies |
|---|---|
| Application | Astro, TypeScript, React, MDX, Tailwind CSS |
| Edge / Security | Cloudflare, DNS Proxy, DDoS & Bot Protection |
| Infrastructure | Linux, VPS, Docker, Docker Compose |
| Networking | Traefik, Nginx, HTTPS, Let's Encrypt |
| CI/CD | GitHub Actions, GHCR, Docker Buildx |
| Security | HMAC-SHA256 deployment authentication |
| Content | Astro Content Collections, MDX |
- Self-hosted production environment running on a Linux VPS
- Cloudflare as the public edge layer, hiding the origin IP and filtering malicious/bot traffic
- Multi-stage Docker builds with a minimal runtime image
- Automated CI/CD with GitHub Actions
- Immutable deployments using Git commit SHA image tags
- Authenticated deployment webhook protected with HMAC-SHA256
- Reverse proxy and TLS with Traefik
- Static content delivery through Nginx
- Git-based content management for the technical blog
corepack enable
pnpm install
pnpm devBuild for production:
pnpm build