- Astro static output with no client-side framework runtime
- Astro content collections for projects
- Bulma + SCSS
- Self-hosted Poppins fonts
- Optimized local images via
astro:assets
bun install
bun run dev # http://localhost:4321
bun run build # static output in dist/
bun run preview # preview the production build
bun run check # Astro type/diagnostic check
bun run lint # ESLint
bun run check:seo # title/meta/canonical/JSON-LD/sitemap/link audit
bun run test:e2e # Playwright smoke tests (builds and serves via Docker Compose)Each project lives in src/content/projects/<slug>.md. The frontmatter contains the card
metadata and the Markdown body is the project page:
---
name: 'Project name'
description: 'Card description'
order: 1
stack: ['react']
cover: '../../assets/projects/project-cover.jpg'
links:
- label: 'View on GitHub'
url: 'https://github.com/...'
target: '_blank'
rel: 'noreferrer'
---Covers live in src/assets/projects/ and are validated/optimized by the content schema.
docker compose up --build -d # http://localhost:8080
docker compose logs -f
docker compose downThe stack builds the Astro site and serves it with an unprivileged Nginx container on port
8080. Override the published host port or canonical site URL with environment variables:
PORT=3000 SITE_URL=https://staging.example.com docker compose up --build -d- Static output is generated with
astro buildand served by Nginx in Docker. - Nginx runs as the unprivileged
nginxuser and listens on port8080inside the container. - Base images are pinned by digest.
- The container filesystem is read-only, all Linux capabilities are dropped, and
no-new-privilegesis enabled. - A Content Security Policy blocks inline scripts and restricts styles, fonts, images, and analytics connections.
- HSTS is emitted by the container; terminate TLS at the reverse proxy and ensure the proxy forwards HTTPS traffic.
- Custom Nginx rules live in
nginx/default.conf. - Common scanner probe paths like
.env,xmlrpc.php,wp-*, and*.phpreturn404. - Dotfiles are blocked except
/.well-known/*. - Hash-based assets in
/_astro/are cached immutably; HTML is served withno-cache. - Unknown routes return the custom
404.htmlpage.
.github/workflows/ci.ymlruns checks, lint, build, the SEO audit, Docker build validation, and Playwright smoke tests on pushes and pull requests..github/dependabot.ymlkeeps npm, Docker base images, and GitHub Actions current.
- Sitemap files are generated automatically by
@astrojs/sitemapduringbun run build. robots.txtis generated fromsrc/pages/robots.txt.tsand points atsitemap-index.xml.- Set
SITE_URLto change the canonical domain if needed. The default ishttps://luisjcenteno.dev.
