Skip to content

GabrielPalmar/Website

Repository files navigation

Gabriel Palmar - Portfolio Website

Personal portfolio website showcasing projects, blog posts, and professional experience as a Solutions Engineer specializing in cloud infrastructure, DevOps, and networking.

Live: gabrielpalmar.com

Features

  • Portfolio - Showcase of selected projects with detailed case studies.
  • Blog - Technical articles and professional insights.
  • About - Career timeline, skills, certifications, and experience.
  • Contact - Professional contact information and availability.
  • Responsive Design - Mobile-first approach with dark theme.
  • SEO Optimized - Auto-generated sitemap and meta tags for easy Google index.

Tech Stack

Category Technology
Framework Astro 5
Language TypeScript
Markdown Marked
Server Caddy 2.11 (Alpine)
Container Docker (multi-stage)
CI/CD GitHub Actions

Astro was used because it's lightweight and its easy implementation aligns with what a portfolio can provide, allowing for the use of templates and modification on the go if needed. Also, does not rely on JS, so its performance is not affected by its static nature. A bit of TypeScript was used to have global files under the data folder.

In addition to containerization for easy Compose launch, a GitHub Actions workflow helps push the image to the GitHub Container Registry.

Architecture

graph TB
    subgraph "Source"
        A[posts.ts<br/>Blog content]
        B[projects.ts<br/>Portfolio data]
        C[Layout.astro<br/>Shared template]
    end

    subgraph "Build Stage - Node.js 25.2.1"
        D[npm install]
        D --> E[astro build]
        E --> F[Static HTML/CSS/JS<br/>/dist]
    end

    subgraph "Runtime Stage - Caddy 2.11"
        F --> G[File Server]
        G --> H[Security Headers]
        H --> I[Gzip Compression]
        I --> J[Asset Caching<br/>30 days]
    end

    subgraph "Pages"
        K[index.astro<br/>Home]
        L[about.astro<br/>About]
        M[contact.astro<br/>Contact]
        N["projects/[slug].astro<br/>Dynamic routes"]
        O["blog/[slug].astro<br/>Dynamic routes"]
    end

    A --> E
    B --> E
    C --> K & L & M & N & O
    K & L & M & N & O --> E

    J --> P[HTTPS<br/>Cloudflare Origin Certificate]
Loading

Project Structure

├── src/
│   ├── data/
│   │   ├── posts.ts          # Blog posts with metadata and content
│   │   └── projects.ts       # Portfolio projects with case studies
│   ├── layouts/
│   │   └── Layout.astro      # Base template (nav, footer, styles)
│   └── pages/
│       ├── index.astro       # Home page with hero and featured projects
│       ├── about.astro       # Timeline, skills, certifications
│       ├── contact.astro     # Contact methods and availability
│       ├── 404.astro         # Not found page
│       ├── projects/
│       │   ├── index.astro   # Projects gallery
│       │   └── [slug].astro  # Dynamic project detail pages
│       └── blog/
│           ├── index.astro   # Blog listing
│           └── [slug].astro  # Dynamic blog post pages
├── public/                   # Static assets (images, favicon, resume)
├── Dockerfile                # Multi-stage build configuration
├── docker-compose.yml        # Container orchestration
├── Caddyfile                 # Web server configuration
└── astro.config.mjs          # Astro configuration with sitemap

Quick Start

Development

npm install
npm run dev       # Starts dev server at localhost:4321

Production Build

npm run build     # Generates static files in /dist
npm run preview   # Preview production build locally

Docker

# Local development (builds from Dockerfile)
docker compose up --build

# Production (pulls from registry)
docker compose pull
docker compose up -d

Environment Variables

Variable Default Description
DOMAIN localhost Domain for Caddy
IMAGE ghcr.io/gabrielpalmar/website:latest Container image to use
TLS_CERT - Path to TLS certificate inside container
TLS_KEY - Path to TLS private key inside container
CERTS_PATH - Host path to certificates directory

Security

Caddy is configured with the following security headers:

  • HSTS - Enforces HTTPS with preload
  • CSP - Restricts content sources to prevent XSS
  • X-Frame-Options - Prevents clickjacking
  • X-Content-Type-Options - Prevents MIME sniffing
  • Referrer-Policy - Controls referrer information
  • Permissions-Policy - Disables unused browser APIs

Deployment

The container exposes ports 80 and 443. TLS is handled via Cloudflare Origin Certificates with Full (strict) SSL mode.

TLS Setup

  1. Generate an Origin Certificate in Cloudflare Dashboard (SSL/TLS → Origin Server)
  2. Save the certificate and key on your server (e.g., /path/to/certs/)
  3. Configure environment variables in your .env file
# Pull and run
docker pull ghcr.io/gabrielpalmar/website:latest

# With Cloudflare Origin Certificate
DOMAIN=gabrielpalmar.com \
CERTS_PATH=/path/to/certs \
TLS_CERT=/certs/origin.pem \
TLS_KEY=/certs/origin-key.pem \
docker compose up -d

Health Check

The /health endpoint returns 200 OK for container health monitoring.

About

Personal portfolio showcasing cloud architecture, DevOps automation, and customer-focused infrastructure solutions.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors