Documents the current pipeline anatomy, the metrics to capture on the first real run, hypothesised bottlenecks, and ranked optimisation proposals. Placeholders for real numbers; to be filled in after the first deploy lands. Companion to the Portainer redeploy step in .gitea/workflows/ci-cd.yaml on the same branch.
David Aragón — Portfolio
Portfolio personal y blog técnico de David Aragón. Sitio estático construido con Astro 5, donde comparto proyectos en los que estoy trabajando (build in public), posts sobre IoT, desarrollo y decisiones técnicas, y una página de about con mi trayectoria.
- URL pública: https://davidaragon.impresion3d.pro
- Autor: David Aragón — de orfebre a fotógrafo a developer
- Repositorio: https://gitlab.impresion3d.pro/root/davidaragon-portfolio
Características
- Home con hero, últimos 3 posts del blog, proyectos destacados y CTA de newsletter
- Blog (
/blog) con posts categorizados entechnical,businessypersonal, con RSS en/rss.xml - Projects (
/projects) con proyectos en estadosactive,developmentocompleted - About (
/about) con la trayectoria profesional completa - Sitemap autogenerado en
/sitemap-index.xmlpor@astrojs/sitemap - Newsletter vía formulario en la home (acción
/api/subscribe) - Type-safe content con Zod schemas en
src/content/config.ts - Búsqueda y SEO: meta tags, Open Graph y RSS listos para compartir
Stack técnico
- Astro 5 — framework de sitios estáticos con islands
- TypeScript — tipado estricto en todo el código
- Tailwind CSS 3 — utility-first CSS con
@tailwindcss/typographypara prose - Zod — validación de frontmatter en content collections
- RSS vía
@astrojs/rss - Sitemap vía
@astrojs/sitemap - Nginx como servidor estático en el contenedor de producción
- Docker multi-stage (Node para build, nginx para servir)
- CI/CD con Gitea Actions en
.gitea/workflows/ci-cd.yaml - Deploy automático a Portainer vía webhook
Quick start (local)
Requisitos: Node.js >= 18.14.1 (recomendado 20).
# 1. Instalar dependencias
npm install
# 2. Arrancar dev server con HMR
npm run dev
# -> http://localhost:4321
# 3. Build de producción (genera ./dist/)
npm run build
# 4. Preview local del build
npm run preview
Despliegue (producción)
El sitio se construye como imagen Docker y se publica en el registry de Gitea. Portainer hace pull y reinicia el contenedor cuando el CI dispara el webhook.
# Construir y servir localmente con Docker Compose
docker compose up --build
# -> http://localhost:8080
# Producción: pull de la imagen publicada por CI
docker compose -f docker-compose.prod.yml up -d
# -> http://localhost:3001
La imagen final (nginx:alpine) solo contiene los estáticos generados en ./dist/. Ver Dockerfile para detalles del multi-stage.
Cómo añadir contenido
Nuevo post de blog
Crea un archivo Markdown en src/content/blog/ con nombre YYYY-MM-DD-slug.md. El frontmatter debe cumplir el schema de src/content/config.ts:
---
title: "Título del post"
description: "Resumen corto para SEO y previews"
publishDate: 2026-07-09
author: "David Aragón" # opcional, default: David Aragón
tags: ["tag1", "tag2"]
category: "technical" # technical | business | personal
featured: false # true para destacar en home
draft: false # true para ocultar del sitio público
image: "/ruta/opcional.jpg" # opcional
---
# Contenido del post en Markdown
Aparecerá automáticamente en /blog/<slug> y, si no es draft, en la home.
Nuevo proyecto
Crea un archivo Markdown en src/content/projects/ con frontmatter:
---
title: "Nombre del proyecto"
description: "Descripción de una línea"
url: "https://proyecto.example.com"
github: "https://github.com/user/repo" # opcional
status: "development" # active | development | completed
tags: ["React", "FastAPI"]
startDate: 2026-07-01
featured: true # true para mostrar en home
image: "/ruta/opcional.jpg" # opcional
---
# Descripción completa del proyecto en Markdown
Aparecerá en /projects/<slug> y, si featured: true, también en la home.
Estructura del proyecto
.
├── astro.config.mjs # Configuración Astro (site, integrations, markdown)
├── tailwind.config.cjs # Tailwind theme y content paths
├── tsconfig.json # TS estricto
├── package.json # Deps y scripts
├── Dockerfile # Multi-stage build (node -> nginx)
├── docker-compose.yml # Local dev/test
├── docker-compose.prod.yml # Producción (pull de registry)
├── nginx.conf # Configuración nginx
├── .gitea/workflows/ # Pipelines CI/CD
│ └── ci-cd.yaml
├── public/ # Assets estáticos servidos tal cual (favicon, david-aragon.jpg, etc.)
└── src/
├── components/
│ ├── layout/ # Header, Footer
│ └── ui/ # Card, Tag
├── content/
│ ├── config.ts # Schemas Zod para blog y projects
│ ├── blog/ # Posts en Markdown
│ └── projects/ # Proyectos en Markdown
├── layouts/ # BaseLayout, BlogLayout, ProjectLayout
├── pages/
│ ├── index.astro # Home
│ ├── about.astro # About
│ ├── blog/ # Index + [slug].astro
│ ├── projects/ # Index + [slug].astro
│ └── rss.xml.ts # Feed RSS (si está generado)
├── styles/ # CSS global
└── utils/ # dateFormat.ts, readingTime.ts
CI/CD
Pipeline definido en .gitea/workflows/ci-cd.yaml. Trigger: push a main.
- Build: checkout, login en el registry de Gitea, build de la imagen Docker multi-stage
- Push: publica
gitlab.impresion3d.pro/root/davidaragon-portfolio:latest - Deploy: el webhook configurado en Portainer hace pull de la nueva imagen y reinicia el contenedor
Secrets necesarios en el repo (Settings → Secrets):
DOCKER_USERNAME— usuario del registry (normalmenteroot)DOCKER_PASSWORD— token personal con permisos de registry
Ver CI-CD-SETUP.md para instrucciones detalladas de Portainer y registro de runners.
Licencia
Código del sitio: ver LICENSE si existe en el repo. El contenido (posts y proyectos) es © David Aragón.
Créditos
- Construido con Astro
- Estilado con Tailwind CSS
- Hospedado en infraestructura propia (Gitea + Portainer)