Skip to content

Self-Hosted Portfolio Site — niklabs.org

Project Type

Personal infrastructure project — self-hosted static portfolio site running on Proxmox homelab, served through Nginx Proxy Manager, published publicly via Cloudflare DNS and SSL.


Project Overview

This project covers the end-to-end setup of niklabs.org — a self-hosted portfolio and documentation site built with MkDocs Material, running on an Ubuntu Server inside the NikLabs Proxmox homelab, and published publicly through Cloudflare with SSL.

The goal was to build a professional-looking technical portfolio site that:

  • Runs entirely on existing homelab infrastructure at zero additional cost
  • Uses markdown files as the content source — no CMS, no database
  • Looks like a modern documentation site (dark mode, search, code copy buttons)
  • Is publicly accessible via a custom domain with valid SSL
  • Can be updated by editing a markdown file and running one build command

Architecture

Visitor → Cloudflare (DNS + SSL) → Home Public IP
→ Router (Port 80 forward) → Nginx Proxy Manager (Docker)
→ Ubuntu Server LXC (Python HTTP server :8085)
→ MkDocs Static Site (/var/www/niklabs-site/site/)

Component Summary

Component Role
MkDocs Material Builds static HTML site from markdown files
Ubuntu Server (Proxmox LXC) Hosts the built static site
Python HTTP server Serves static files on port 8085
Systemd service Keeps Python server running on boot
Nginx Proxy Manager Reverse proxy — routes niklabs.org to Ubuntu server
Cloudflare DNS, SSL termination, DDoS protection
niklabs.org Custom domain

Environment

Attribute Value
Hosting Platform Proxmox homelab
Server Ubuntu 22.04 LXC (k3s-worker2)
Server IP 192.168.1.246
Site Port 8085
Static Site Generator MkDocs Material 9.x
Python Version 3.12
Reverse Proxy Nginx Proxy Manager (Docker container)
DNS / SSL Cloudflare (Flexible SSL mode)
Domain niklabs.org

Implementation

1. Install MkDocs on Ubuntu Server

apt update && apt upgrade -y
apt install python3 python3-pip -y
pip3 install mkdocs-material --break-system-packages
mkdocs --version

2. Create the MkDocs Project

mkdir -p /var/www
cd /var/www
mkdocs new niklabs-site
cd niklabs-site

Project structure created:

niklabs-site/
├── docs/
│   └── index.md
└── mkdocs.yml

3. Configure mkdocs.yml

site_name: NikLabs
site_url: https://niklabs.org
site_author: Nikhil Seth
site_description: IT Infrastructure & Cloud Portfolio

theme:
  name: material
  palette:
    - scheme: slate
      primary: blue
      accent: cyan
      toggle:
        icon: material/weather-sunny
        name: Switch to light mode
    - scheme: default
      primary: blue
      accent: cyan
      toggle:
        icon: material/weather-night
        name: Switch to dark mode
  features:
    - navigation.tabs
    - navigation.sections
    - navigation.top
    - navigation.indexes
    - search.highlight
    - search.suggest
    - content.code.copy
    - toc.follow

nav:
  - Home: index.md
  - Projects:
    - projects/index.md
    - Hyper-V Failover Cluster: projects/hyperv-cluster.md
  - Homelab:
    - homelab/index.md
  - About: about.md

markdown_extensions:
  - admonition
  - pymdownx.superfences
  - pymdownx.highlight:
      anchor_linenums: true
  - pymdownx.tabbed:
      alternate_style: true
  - pymdownx.details
  - attr_list
  - tables
  - toc:
      permalink: true

extra:
  social:
    - icon: fontawesome/brands/github
      link: https://github.com/Nikhilse120
    - icon: fontawesome/brands/linkedin
      link: https://www.linkedin.com/in/nikhil-seth-9a798350/

4. Create Content Pages

# Projects section
mkdir -p docs/projects
mkdir -p docs/homelab

Pages created:

  • docs/index.md — Homepage
  • docs/about.md — About / certifications / skills
  • docs/projects/index.md — Projects listing
  • docs/projects/hyperv-cluster.md — First project write-up
  • docs/homelab/index.md — Homelab overview

5. Build the Static Site

cd /var/www/niklabs-site
mkdocs build

Output goes to site/ folder — this is the deployable static HTML.


6. Create Systemd Service

To keep the site running permanently across reboots:

nano /etc/systemd/system/niklabs.service
[Unit]
Description=NikLabs MkDocs Site
After=network.target

[Service]
WorkingDirectory=/var/www/niklabs-site/site
ExecStart=python3 -m http.server 8085
Restart=always
User=root

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable niklabs
systemctl start niklabs
systemctl status niklabs

Verified running:

● niklabs.service - NikLabs MkDocs Site
     Active: active (running)
     Main PID: 14902 (python3)

7. Configure Nginx Proxy Manager

In NPM web UI → Hosts → Proxy Hosts → Add Proxy Host:

Field Value
Domain Names niklabs.org
Scheme http
Forward Hostname / IP 192.168.1.246
Forward Port 8085
Cache Assets On
Block Common Exploits On

8. Cloudflare DNS Configuration

Added DNS records in Cloudflare dashboard:

Type Name Content Proxy
A @ HOME-PUBLIC-IP Proxied ON
A www HOME-PUBLIC-IP Proxied ON

SSL/TLS settings:

Setting Value
SSL Mode Flexible
Always Use HTTPS ON
Minimum TLS Version TLS 1.2
Automatic HTTPS Rewrites ON

9. Router Port Forward

Forwarded port 80 on home router → NPM container host IP.

NPM was already handling port 80 for other homelab services — added niklabs.org as an additional proxy host.


Update Workflow

Every time a new project or page is added:

# 1. Edit or add markdown file
nano /var/www/niklabs-site/docs/projects/new-project.md

# 2. Add to nav in mkdocs.yml
nano /var/www/niklabs-site/mkdocs.yml

# 3. Rebuild and restart
cd /var/www/niklabs-site
mkdocs build
systemctl restart niklabs

Site updates in under 10 seconds.


Troubleshooting

Browser showing "page cannot be displayed"

  • Browser was forcing HTTPS on a plain HTTP server
  • Fix: access via http:// not https:// for local testing
  • Cloudflare handles HTTPS termination for public access

Port already in use on first run

  • Manually ran python3 -m http.server 8085 before creating the systemd service
  • Port was already bound when systemd tried to start
  • Fix: killed the manual process, let systemd manage it exclusively

mkdocs.yml nav mismatch

  • File was named hyperv-cluster-mkdocs.md but nav referenced hyperv-cluster.md
  • Fix: renamed file to match nav entry
mv docs/projects/hyperv-cluster-mkdocs.md docs/projects/hyperv-cluster.md

Key Learnings

  • MkDocs Material produces a professional documentation site with zero frontend development
  • Python's built-in HTTP server is sufficient for serving static files in a homelab context
  • Systemd services are the correct way to keep processes running across reboots
  • Cloudflare Flexible SSL mode allows plain HTTP on the origin server while providing HTTPS to visitors
  • Nginx Proxy Manager simplifies routing multiple domains/services without touching raw Nginx config
  • The entire stack costs nothing beyond the domain — all software is free and runs on existing hardware

Tools & Technologies

Category Technology
Static Site Generator MkDocs Material 9.x
Content Format Markdown (.md)
Web Server Python HTTP server (built-in)
Process Manager Systemd
Reverse Proxy Nginx Proxy Manager (Docker)
DNS & SSL Cloudflare
Hosting Platform Proxmox LXC (Ubuntu 22.04)
Domain niklabs.org

Outcome

A fully self-hosted portfolio site running at niklabs.org with:

  • Dark/light mode toggle
  • Full-text search across all pages
  • Code blocks with copy buttons
  • Left navigation sidebar
  • Right-side table of contents per page
  • Mobile responsive
  • Valid SSL via Cloudflare
  • Zero ongoing hosting cost
  • Update workflow under 10 seconds

Built and hosted entirely on the NikLabs homelab — Proxmox, Ubuntu, Docker, and Cloudflare.