WebUI Manager - A self-hosted dashboard for your dashboards

So here's the thing - if you're into self-hosting, you already know what's up. You've got a server - or maybe a few - and you're running all kinds of stuff on it - media servers, monitoring stacks, container managers, download clients, home automation, you name it.

At first, you remember them, but at a certain point, you've got a few too many services running, and you start asking yourself: "Wait, what port was that thing on again?" And so you make a note somewhere. Maybe it's a text file. Maybe it's a bookmark folder, but as if I need more bookmakrs. For me, it was a spreadsheet - and honestly, I got fed up with it blinding me at 1 am due to a lack of a proper dark mode.

So I sat down one Sunday afternoon and built something tiny and lightweight to solve that specific problem. That's WebUI Manager.


What It Is

WebUI Manager is a small, self-hosted Flask app that gives you a clean dashboard for all your internal web services. You add your services, optionally assign them to a host and a category, and they show up in a grouped, filterable view that you can actually navigate without squinting at a spreadsheet.

It's not trying to be a full-on Heimdall or Homer alternative with a million integrations - it's just a clean, practical way to keep track of what's running where, with a couple of useful extras thrown in.


The Features

Service dashboard grouped by host - Your services are organized under whichever server or machine they're running on. At a glance, you can see exactly what each host is running.

Full-text search and filtering - Search by name, URL, description, host, or category. You can also filter the whole dashboard down to a specific host or category tag in one click.

Automatic favicon discovery - The app fetches and caches the favicon for each service automatically. It parses <link rel="icon"> tags from the service's HTML and stores the URL so it doesn't have to fetch it again on every load.

Optional credential storage - You can optionally store a username and password for each service. These are encrypted at rest in the database using Fernet (AES symmetric encryption). There's a reveal button on each card to show the credentials when you need them. Worth noting: this encryption is reversible by design - it's a convenience feature, not a secrets vault. Don't treat it like one.

Host and category management - You can create, edit, and delete hosts and categories through the UI. If a host or category is still in use by a service, the app won't let you delete it until you remove the dependency first.

First-run admin bootstrap - On the first launch with an empty database, the app prompts you to create an admin user and set a password. That password is hashed and can't be reversed. The encryption key for stored credentials is derived from your SECRET_KEY (or a separate APP_CREDENTIALS_KEY if you set one).

Automatic schema creation - Tables are created automatically on the first request. No SQL files to run manually, no migration scripts to worry about.


The Stack

The backend is Flask with SQLAlchemy for the ORM, backed by MySQL or MariaDB. The frontend is Tailwind CSS with vanilla JavaScript - no frameworks, no build step on the client side. The UI has a dark theme with a cyan accent palette. Fonts are IBM Plex Sans and Space Grotesk, icons are Font Awesome.

The app ships as a Docker image published to Docker Hub at nullata/webui-manager.


Setting It Up

You need a MySQL or MariaDB database. Create it and a user for the app:

CREATE DATABASE IF NOT EXISTS webui_manager
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

CREATE USER IF NOT EXISTS 'webui'@'%' IDENTIFIED BY 'your_password_here';
GRANT ALL PRIVILEGES ON webui_manager.* TO 'webui'@'%';
FLUSH PRIVILEGES;

Then set up your .env file. The minimum you need:

SECRET_KEY=something-long-and-random
DB_USER=webui
DB_PASSWORD=your_password_here
DB_HOST=your-db-host
DB_NAME=webui_manager

Docker Compose - pre-built image

services:
  app:
    image: nullata/webui-manager:latest
    pull_policy: always
    restart: unless-stopped
    ports:
      - "${APP_PORT:-5000}:5000"
    env_file:
      - .env
docker compose up -d

Docker Compose - full stack with MariaDB

If you want Compose to manage the database as well:

services:
  db:
    image: mariadb:11
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpassword}
      MARIADB_DATABASE: ${DB_NAME:-webui_manager}
      MARIADB_USER: ${DB_USER:-webui}
      MARIADB_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql

  app:
    image: nullata/webui-manager:latest
    pull_policy: always
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - "${APP_PORT:-5000}:5000"
    environment:
      SECRET_KEY: ${SECRET_KEY}
      APP_CREDENTIALS_KEY: ${APP_CREDENTIALS_KEY:-}
      DB_HOST: db
      DB_PORT: ${DB_PORT:-3306}
      DB_USER: ${DB_USER:-webui}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: ${DB_NAME:-webui_manager}
      AUTO_MIGRATE: ${AUTO_MIGRATE:-true}

volumes:
  db_data:
docker compose up -d

Once it's running, navigate to http://your-host:5000. If no admin user exists yet, the app will redirect you to the setup page automatically.


Environment Variables

Variable Required Default Description
SECRET_KEY Yes - Flask session signing key
DB_USER Yes - MySQL/MariaDB username
DB_PASSWORD Yes - MySQL/MariaDB password
DB_HOST No 127.0.0.1 Database host
DB_PORT No 3306 Database port
DB_NAME No webui_manager Database name
DATABASE_URL No - Full SQLAlchemy URL, overrides all DB_* fields
APP_CREDENTIALS_KEY No Falls back to SECRET_KEY Separate key for credential encryption
AUTO_MIGRATE No true Auto-create tables on first request
APP_PORT No 5000 Port exposed by the Docker container

One Thing to Keep in Mind

Apparently this needs to be said, well - written, but this app is designed to run on your internal network, not exposed to the open internet. The credential storage is encrypted, but the encryption is reversible - it's meant to save you a few clicks, not to be a production secrets manager. Keep it behind a firewall or a VPN, and you're going to be fine.


Source and Image

Licensed under the Apache License 2.0.

Share this article

Copied!

Join the conversation

Like & Comment on