AstroPortfolioBlog

stefanbraum.de — Astro Portfolio Template

By braum-me

Source code for a personal portfolio and blog site built on Astro 5 SSR with the Node adapter, featuring a forest-green dark theme, German/English bilingual support, and dynamic OG image generation, self-hostable via Docker.

Explore the design

Compare the page layouts and mobile experience. Open any screenshot for a closer look.

3 previews
Desktop homepage1 / 3
Desktop captureTop of page

Actual screenshot captured by the server browser.

All pages & views 3

Screenshots show visual appearance; interactive behavior may differ.

Recorded 2026-09-30View source

What stands out

A quick read of the recorded project description.

01

Astro 5 SSR with the @astrojs/node standalone adapter and TypeScript strict mode

02

Tailwind 4 forest-green dark theme with self-hosted Inter, Geist, and JetBrains Mono variable fonts

03

Bilingual i18n with German as default and English under /en/, with dictionary structure kept consistent by types

04

Blog managed as Markdown articles via Astro Content Collections, with RSS output

05

Dynamic OG images generated at /api/og.png via Satori and @resvg/resvg-js

06

Includes a Dockerfile and docker-compose example, supporting Coolify self-hosted deployment

Template Details

This repository contains the complete source code for the personal site stefanbraum.de, positioned as a combined portfolio and blog. The stack centers on Astro 5, using the @astrojs/node adapter in standalone mode for server-side rendering, TypeScript in strict mode, and styling provided by Tailwind 4 via @tailwindcss/vite. The site's default language is German (root path /), with an English mirror under /en/; UI copy lives in src/i18n's de.ts and en.ts, and a Translations type enforces that both dictionaries share the same structure. Blog post bodies themselves are not language-specific, and the /en/blog listing page explicitly notes that posts are in German. Blog content is managed as Markdown articles via Astro Content Collections, while src/data holds typed data for cases, skills, timeline, and certifications. Visually, the site uses a forest-green dark theme, self-hosts Inter, Geist, and JetBrains Mono variable fonts, uses GSAP and ScrollTrigger for entrance animations, and implements an ambient shader and skill orb effect with lazy-loaded Three.js. Dynamic OG images are generated by Satori with @resvg/resvg-js at the /api/og.png endpoint. Code formatting and linting are handled by Biome, and pnpm is used for package management. For deployment, a Dockerfile is provided, building a non-root Node 22 Alpine image; the standalone server reads HOST and PORT from environment variables, defaults to port 4321 inside the container, has no persistent state, and logs to stdout. It can also be deployed to Coolify following the README steps. The README also states that the MIT license covers only code and configuration, while content such as blog posts, case descriptions, and i18n copy remains the author's copyright; if forking as a template, you must replace it with your own content.

AstroPortfolioBlog

Is it right for your project?

Use Cases

  • Building a combined personal portfolio and blog site to showcase cases, skills, timeline, and certifications
  • A personal website that needs German and English UI but keeps blog bodies in a single language
  • Developers who want to self-host an Astro SSR site via Docker or Coolify
  • Blog projects that need dynamically generated social-share OG images and an RSS feed

Suitable For

  • Front-end developers familiar with Astro, TypeScript, and Tailwind
  • Users who want to publish a personal site based on MIT-licensed code after replacing it with their own content
  • Personal projects that need a self-hosted, stateless container deployment

Getting started & customization

Use these recorded setup notes alongside the repository instructions for your chosen version.

  1. 1

    Install dependencies with pnpm; pnpm dev starts the development server at http://localhost:5556 by default

  2. 2

    Copy .env.example to .env; in production, at least set PUBLIC_SITE_URL for canonical, OG, and RSS

  3. 3

    Optionally configure UMAMI_SCRIPT_URL and UMAMI_WEBSITE_ID to connect self-hosted Umami analytics

  4. 4

    The Docker image runs as non-root Node 22 Alpine; the standalone server reads HOST and PORT, with a default container port of 4321

  5. 5

    The MIT license covers only code and configuration; content such as blog posts, case descriptions, and i18n copy must be replaced with your own

Read repository instructions

License & source material

Usage conditions & limitations

Free to use · keep license notice

This entry covers the free version in the public repository under the MIT license; the free scope is the code of this version and does not mean external services or third-party assets are free.

Must comply with when using

  • You may use, modify, commercialize, and distribute this version's code for free.
  • When distributing copies or substantial portions of the code, retain the original author's copyright notice and the full MIT license notice.
  • The code is provided as is; warranties and limitations of liability are governed by the original license and applicable law.

Additional costs & items to verify

  • The MIT license covers only code and configuration; content such as blog posts, case descriptions, and i18n copy remains the author's copyright and is not licensed with the code.
  • If using this project as a template fork, you must replace the blog posts in src/content/blog/, the case descriptions in src/data/, and the copy in src/i18n/ with your own content.
  • Optional self-hosted Umami analytics requires you to provide the Umami script URL and site ID; not configuring it does not affect site operation.
  • Demo images, fonts, icons, and other third-party assets must be checked against their respective licenses and cannot be judged solely by the code's MIT license.
  • External services such as domains, hosting, databases, payments, or AI may incur separate charges.
View Original Text Basis

Permission is hereby granted, free of charge

Original License Text

Die MIT-Lizenz deckt nur Code und Konfiguration ab.

Original README

bitte den Content (Blog-Posts in `src/content/blog/`, Case-Beschreibungen in `src/data/`, Texte in `src/i18n/`) durch eigenen ersetzen

Original README

UMAMI_SCRIPT_URL

Original README
https://github.com/braum-me/stefanbraum.de/blob/main/LICENSEhttps://github.com/braum-me/stefanbraum.de/blob/main/README.md

Compiled by DeepSeek based on repository license and README ·2026-10-01. Results only cover the materials read; dependencies, images, fonts, and subsequent term changes are subject to their respective original texts.

Project checks & description sources

Template Information

Source platforms
GitHub
Documentation and maintenance reference score
More documented · 85 / 100
Technologies
Astro
Website type
Portfolio · Blog
How this score is calculated

Our heuristic uses license records (up to 30), a demo link (12), a preview (10), stars (15), recorded maintenance (15), description completeness (8), feature records (5) and classification data (5). It is not a code, security or performance test, or a Google score. A link does not establish tested functionality.

What to verify for this project

Repository update recorded
2026-09-28
Screenshot last checked
2026-10-01

A demo link is listed. Check it with your own content and viewport sizes; its presence does not confirm that forms, authentication or payments work.

The catalog identifies Astro as the primary stack. Check the actual dependency manifest and lockfile for versions and hosting requirements. These notes use recorded metadata, not a code audit or a deployment test.

Use a selection guide →

How this description was prepared

AI-assisted description based on catalog records and repository material. Repository authors are not automatically the reviewers of this description.

Automatically checked against source and license rules; no manual editorial review.

Inspect the recorded source material

SHA-256: e470ac906a5cbdc2e77721dca2c2fc2d7f413d02785a14db291e4b04239a6a6e

2026-10-01T13:33:33.311Z

Source file / repository
# stefanbraum.de

Personal site source. Astro 5 SSR with the Node adapter, TypeScript strict, Tailwind 4. Forest-green dark theme, two locales (`de` default, `en` under `/en/`), self-hosted blog, dynamic OG-image generation.

**Live:** [stefanbraum.de](https://stefanbraum.de)

![Preview](.github/preview.jpg)

## Stack

- [Astro 5](https://astro.build) with `@astrojs/node` (standalone mode)
- TypeScript strict (`noUnusedLocals`, `noUncheckedIndexedAccess`)
- Tailwind 4 via `@tailwindcss/vite`
- Self-hosted variable fonts (Inter, Geist, JetBrains Mono)
- [Satori](https://github.com/vercel/satori) + `@resvg/resvg-js` for dynamic OG images
- GSAP + ScrollTrigger for entry animations
- Three.js (lazy-loaded) for ambient shader and skill sphere
- Astro Content Collections for the blog (Markdown)
- [Biome](https://biomejs.dev) for format + lint
- pnpm

## Local development

```bash
pnpm install
pnpm dev          # http://localhost:5556
pnpm check        # astro check (type errors, accessibility hints)
pnpm build        # production build
pnpm lint         # biome
pnpm format       # biome format --write
```

## Project layout

```
src/
├─ components/      # Astro components (Hero, Cases, Skills, etc.)
├─ content/blog/    # Markdown posts (Astro Content Collection)
├─ data/            # Typed data (cases, skills, timeline, certifications)
├─ i18n/            # Locale dictionaries (de, en) + helpers
├─ layouts/         # BaseLayout
├─ pages/           # Route handlers
│  ├─ api/og.png.ts # Dynamic OG image endpoint
│  ├─ blog/         # Blog listing + detail
│  ├─ en/           # English mirror routes
│  └─ rss.xml.ts    # RSS feed
├─ scripts/         # Client-side animation
└─ styles/global.css
```

## Environment

Copy `.env.example` to `.env` and fill the values you need. All variables are optional except `PUBLIC_SITE_URL` for production.

| Variable           | Purpose                                            |
| ------------------ | -------------------------------------------------- |
| `PUBLIC_SITE_URL`  | Canonical site URL (used in canonical, OG, RSS)    |
| `UMAMI_SCRIPT_URL` | Self-hosted Umami script URL (analytics, optional) |
| `UMAMI_WEBSITE_ID` | Umami site ID (analytics, optional)                |

## Deploy

The included `Dockerfile` produces a non-root, production-ready Node 22 Alpine image. The standalone server reads `HOST` and `PORT` from the environment.

### Container basics

- Internal port: `4321` (Astro Node-Adapter standalone default)
- Bind host: `0.0.0.0` (set via `HOST` env)
- Healthcheck endpoint: `/` returns 200

### docker-compose

```yaml
services:
  web:
    build: .
    environment:
      HOST: 0.0.0.0
      PORT: 4321
      PUBLIC_SITE_URL: https://example.com
    ports:
      - "4321:4321"
```

### Coolify

1. New Resource → Public Repository → repository URL
2. Branch: `main`
3. Build pack: `Dockerfile` (auto-detected)
4. Port mapping: container exposes `4321`
5. Environment variables: at minimum set `PUBLIC_SITE_URL`. Coolify injects `HOST=0.0.0.0` automatically; otherwise add it explicitly
6. Deploy

The container has no persistent state. Logs go to stdout.

## Internationalization

Astro 5 native i18n.

- `de` (default) → `/`
- `en` → `/en/`

UI strings live in `src/i18n/de.ts` and `src/i18n/en.ts`. Blog post bodies are language-neutral; the listing on `/en/blog` carries an explicit notice that posts are in German.

To translate a string, edit both dictionaries. The `Translations` type in `de.ts` enforces structural parity.

## License

MIT — see [LICENSE](./LICENSE).

Inhalte (Briefings, Blog-Posts, Texte): © 2026 Stefan Braum, alle Rechte vorbehalten. Die MIT-Lizenz deckt nur Code und Konfiguration ab. Wer den Stack als Template forken will: gerne, aber bitte den Content (Blog-Posts in `src/content/blog/`, Case-Beschreibungen in `src/data/`, Texte in `src/i18n/`) durch eigenen ersetzen.
Report a correction

Explore similar templates

Compare previews and descriptions within the same stack and category.