A fast, bilingual (ES/EN) personal portfolio website built with Astro and deployed to a self-hosted VPS via Docker + nginx with a fully automated GitHub Actions CI/CD pipeline.
🔗 Live: https://www.a-robertdev.com
- Bilingual ES / EN — instant language toggle (no page reload). Content is authored with
data-en/data-esattributes and the chosen language is persisted inlocalStorage. - Dark mode — automatic theme that honors the visitor's system preference (
prefers-color-scheme) using CSS custom properties. - Responsive — mobile-first layout with fluid typography (
clamp()) and a properly set viewport, looks great from phones to desktops. - SEO-ready — descriptive
<title>and meta description, Open Graph tags, a generatedrobots.txt, sitemap reference and per-bot crawl rules viaastro-robots-txt. - Fast & lightweight — zero client-side framework; Astro ships static HTML/CSS with only a tiny vanilla-JS snippet for the language toggle.
- CV downloads — bundled ES/EN résumé PDFs served as static assets.
- Production deployment — multi-stage Docker build (Node for building, nginx for serving) with a container
HEALTHCHECK. - CI/CD to VPS — push to
maintriggers a GitHub Actions workflow that builds, deploys over SSH, tags a rollback backup image, runs a health check, prunes old images, and sends Telegram notifications on start / success / failure. - Ops tooling — helper scripts for health checks (
scripts/health-check.sh) and rollback (scripts/rollback.sh).
| Layer | Technology |
|---|---|
| Framework | Astro 5 |
| Language | TypeScript |
| Styling | Hand-written CSS with custom properties (no UI framework) |
| Fonts | Inter |
| SEO | astro-robots-txt (robots.txt + sitemap) |
| Web server | nginx (alpine) |
| Container | Docker (multi-stage) + Docker Compose |
| CI/CD | GitHub Actions → SSH deploy to VPS |
Requirements: Node.js 22+ and npm.
# Install dependencies
npm install
# Start the dev server (http://localhost:4321)
npm run dev
# Type-check and build for production (output in ./dist)
npm run build
# Preview the production build locally
npm run preview# Build the image and start the container (served on http://localhost:4321)
docker compose up --build.
├── src/
│ ├── pages/
│ │ └── index.astro # Single-page portfolio (ES/EN content, styles, lang toggle)
│ └── env.d.ts
├── public/ # Static assets (favicon, robots.txt, CV PDFs)
├── scripts/ # Ops helpers (health-check.sh, rollback.sh, check-wakatime.js)
├── docs/ # Additional project documentation
├── .github/workflows/ # CI/CD: build + deploy to VPS
├── astro.config.mjs # Astro config (site URL, robots.txt/sitemap)
├── Dockerfile # Multi-stage build (Node → nginx)
├── docker-compose.yml
└── nginx.conf # Server config + rate limiting
Deployment is fully automated. On every push to main, GitHub Actions:
- Installs dependencies and runs
npm run build(type-check + build). - Connects to the VPS over SSH, pulls the latest code and rebuilds the Docker image.
- Tags the previous image as a timestamped backup (for quick rollback).
- Recreates the container with zero-downtime and verifies it's healthy.
- Cleans up old images and notifies via Telegram (start / success / failure).
Add a screenshot of the homepage here (light or dark mode) once captured — see the placeholder comment near the top of this README. Save it under
public/and reference it with a relative path.
See LICENSE. MIT is recommended for this codebase.
Built by Robert Ruben · a-robertdev.com