Heimdall Application Dashboard: Set Up a Sleek Homelab Portal in Docker

Complete guide to self-hosting Heimdall application dashboard using Docker Compose. Set up widgets and links to organize your home network.

Heimdall Application Dashboard: Set Up a Sleek Homelab Portal in Docker

Managing multiple self-hosted services, tools, and platforms on a VPS or home server quickly becomes chaotic without a central portal. Heimdall is a lightweight, elegant application dashboard that acts as a single point of entry for your homelab. It features active API integrations (Enhanced Apps) that display live statistics from services like Pi-hole, Portainer, or qBittorrent directly on the dashboard.

This guide provides a comprehensive walkthrough for deploying Heimdall on a VPS using Docker Compose. We cover state persistence, reverse proxy configurations, layout customization, and essential security hardening.

Architecture and Volume Mapping

To ensure data persistence and ease of backup, you must understand Heimdallโ€™s internal storage structure. Heimdall runs as a Laravel PHP application inside the container. The official LinuxServer image simplifies configuration by grouping all application state under a single volume path: /config.

Inside /config, the following structure is dynamically generated on first boot: - /config/www/: The web root containing the PHP application and custom layouts. - /config/www/database/app.sqlite: The SQLite database containing all item configurations, settings, user profiles, and layouts. - /config/nginx/: Config files for the container's internal Nginx web server. - /config/keys/: Automatically generated SSL/TLS keys for the internal web server.

User and Group Permissions (PUID/PGID)

Like all LinuxServer.io containers, Heimdall implements PUID (Process User ID) and PGID (Process Group ID) environment variables. Setting these ensures the files generated inside the container are owned by your host system's non-root user, preventing file permission conflicts when editing configurations or running backups.

To find your system's current user IDs, run:

id $USER

Typically, the default non-root user will have uid=1000(user) and gid=1000(user).


Docker Compose Setup

Create a dedicated directory for Heimdall and define the service in a docker-compose.yml file.

1. Create the Directory Structure

mkdir -p ~/homelab/heimdall
cd ~/homelab/heimdall

2. Configure docker-compose.yml

Create a file named docker-compose.yml with the following contents:

version: '3.8'

services:
  heimdall:
    image: lscr.io/linuxserver/heimdall:latest
    container_name: heimdall
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
    volumes:
      - ./config:/config
    ports:
      - "8080:80"
      - "8443:443"

3. Start the Container

Run the following command to start Heimdall in the background:

docker compose up -d

Docker will pull the image and construct the directories under ./config.


Reverse Proxy Configuration

Exposing raw ports (like 8080 or 8443) directly to the internet is insecure and bad practice. Instead, set up a reverse proxy with TLS encryption (HTTPS).

Nginx Configuration

If you are using Nginx on your VPS, create a virtual host configuration file at /etc/nginx/sites-available/heimdall and symlink it to /etc/nginx/sites-enabled/.

server {
    listen 80;
    listen [::]:80;
    server_name portal.example.com;

    # Redirect all HTTP traffic to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name portal.example.com;

    # SSL Certificate Paths
    ssl_certificate /etc/letsencrypt/live/portal.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portal.example.com/privkey.pem;

    # Secure SSL Protocols and Ciphers
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';

    # Security Headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support (if needed by underlying dashboard apps)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

Caddy Configuration

For a simpler setup, Caddy handles automated Let's Encrypt certificate acquisition and renewal. Use the following block in your Caddyfile:

portal.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }

    header {
        Strict-Transport-Security "max-age=31536000;"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer-when-downgrade"
    }
}

Configuring Layouts, Custom Styles, and Databases

Heimdall keeps dashboard layouts highly organized through tag groups and CSS templates.

SQLite Database Management

Since Heimdall stores dashboard links and configurations inside /config/www/database/app.sqlite, you can perform automated backups by simple file replication. If you want to backup the application config, run a daily cron job that safely copies the database file:

# SQLite Backup Command
sqlite3 /path/to/heimdall/config/www/database/app.sqlite ".backup '/path/to/backups/heimdall_backup_$(date +%F).sqlite'"

Injecting Custom CSS & JavaScript

Heimdall permits custom CSS injection to override default styling (e.g., configuring glassmorphism, transparent backgrounds, or adjusting grid widths).

Edit the custom CSS file on the host machine:

nano /path/to/heimdall/config/www/css/custom.css

To implement a clean glassmorphism styling across the application cards, add the following CSS rules:

/* Glassmorphism style for Heimdall app cards */
.item-container.card {
    background: rgba(255, 255, 255, 0.1) !important;
    backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 12px;
    box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.item-container.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
    background: rgba(255, 255, 255, 0.15) !important;
}

Setting Up Enhanced Apps (Widgets)

Heimdall widgets go beyond simple static bookmarks. For supported applications (e.g., Pi-hole, qBittorrent, Radarr, Sonarr, Portainer, or Home Assistant), Heimdall can call the service's API and display live metrics on the card.

To configure an Enhanced App: 1. Click Add Application in Heimdall. 2. In the Application Name dropdown, select a supported service (e.g., Pi-Hole). 3. Fill in the URL (the local or reverse proxied address accessible by the server/browser). 4. Check the Config tab on the left. In this tab, you will find settings specific to the API. 5. Provide the required credentials (e.g., Pi-hole API Token, or qBittorrent Username/Password). 6. Click Save. The widget will automatically poll the service at regular intervals.


Security Hardening for VPS Deployments

Deploying Heimdall on a public VPS exposes your internal portal link directories. Protect it using these security mechanisms.

1. Enable Built-In Authentication

By default, Heimdall allows anyone who visits the dashboard to edit settings or view apps. - Go to the top-right menu and select Users. - Edit the default user (or add a new administrator account) and set a secure password. - Go to the Settings menu and enable the Require login to view dashboard option.

2. Limit Reverse Proxy Rate Limits (Nginx)

To prevent brute-force attacks on the Heimdall login page, implement rate limiting in Nginx:

# Add in the http block of /etc/nginx/nginx.conf
limit_req_zone $binary_remote_addr zone=heimdall_limit:10m rate=5r/s;

# Add in the location block of your virtual host configuration
location / {
    limit_req zone=heimdall_limit burst=10 nodelay;
    proxy_pass http://127.0.0.1:8080;
    # ... other proxy headers
}

By enforcing SSL/TLS encryption, setting up API widget cards for enhanced data, and securing authentication, you establish a resilient, production-ready landing page for all of your homelab activities.