webui-manager

webui-manager

apps.detail.types.app from nullata's Repository

apps.detail.sections.overview

A self-hosted Flask app for organizing and launching your internal web services. Stores service URLs grouped by host with optional credentials, category tags, and auto-discovered favicons.

webuimanager-logo

logo WebUI Manager

A self-hosted Flask app for organizing and launching your internal web services. Stores service URLs grouped by host with optional credentials, category tags, and auto-discovered favicons.

Features

  • Session-based login with first-run admin bootstrap
  • Admin password change from the Settings page
  • Login lockout after 5 failed attempts (5-minute cooldown, resets on restart)
  • CSRF protection on all forms and AJAX requests
  • Dashboard grouped by host with favicon auto-discovery
  • Optional background health checks with per-app healthcheck endpoints
  • Full-text search across name, URL, description, host, and category, with optional live (as-you-type) filtering
  • Ctrl+K / Cmd+K to jump to the search box
  • Filter by host or category
  • Optional stored credentials (AES-encrypted at rest), copyable without revealing them
  • Export and import the whole catalog as JSON, for backup or bulk-adding services
  • Background image upload with MIME type validation and 10 MB size limit
  • MySQL/MariaDB or self-contained SQLite backend, with automatic schema creation and migration on startup

Service Types

Each entry has a service type that controls how the dashboard renders it.

Type URL required Favicon Description
Web UI Yes Auto-discovered A browser-accessible interface - Grafana, Portainer, Jellyfin, etc.
API Yes None An HTTP API or backend service - REST APIs, internal endpoints, etc.

Requirements

  • Python 3.12+
  • MySQL/MariaDB (default) — or use SQLite for self-contained deployments

Setup

MySQL / MariaDB (default)

# 1. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

# 2. Install dependencies
pip install -r requirements.txt

# 3. Configure environment
cp .env.example .env
# Edit .env with your DB credentials and a strong SECRET_KEY

# 4. Create the database and user
mysql -u root -p <<'SQL'
CREATE DATABASE IF NOT EXISTS webui_manager
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'webui'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL PRIVILEGES ON webui_manager.* TO 'webui'@'localhost';
FLUSH PRIVILEGES;
SQL
# Then set DB_USER=webui, DB_PASSWORD=changeme (or your chosen values) in .env

# 5. Run
flask --app run.py run

Tables are created automatically on startup. Navigate to / and follow the admin setup prompt.

SQLite (self-contained, no external database)

For single-user or simple deployments, set DB_TYPE=sqlite in your .env (or as an environment variable). The database file is stored at DB_PATH (default: data/webui_manager.sqlite).

# 1. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

# 2. Install dependencies
pip install -r requirements.txt

# 3. Configure environment
cp .env.example .env
# Edit .env — set SECRET_KEY and ensure DB_TYPE=sqlite

# 4. Run (database file created automatically)
flask --app run.py run

The SQLite file is created automatically under data/webui_manager.sqlite on startup. Tables and schema are managed the same way as MySQL — auto-created and auto-migrated.

Environment Variables

Variable Required Description
SECRET_KEY Yes Flask session signing key
DB_TYPE No Database backend: mysql (default) or sqlite
DB_USER Conditional MySQL username (required when DB_TYPE=mysql)
DB_PASSWORD Conditional MySQL password (required when DB_TYPE=mysql)
DB_HOST No MySQL host (default: 127.0.0.1)
DB_PORT No MySQL port (default: 3306)
DB_NAME No Database name (default: webui_manager)
DB_PATH No SQLite file path (default: data/webui_manager.sqlite)
DATABASE_URL No Full SQLAlchemy URL, overrides DB_TYPE and all DB_* fields
APP_CREDENTIALS_KEY No Separate key for credential encryption (falls back to SECRET_KEY)
AUTO_MIGRATE No Sync existing tables to the current schema on startup (default: true). Missing tables are always created regardless

Docker Hub

The image is published at nullata/webui-manager.

docker pull nullata/webui-manager

Or use it directly in your docker-compose.yml:

image: nullata/webui-manager

Docker Deployment

1. Configure environment

cp .env.example .env
# Edit .env - set SECRET_KEY, DB_PASSWORD, and any other values

2a. Docker Compose - build from source

The included docker-compose.yml builds the image locally. You will need an external MySQL/MariaDB instance reachable from the container; update DB_HOST in .env accordingly (or skip the external database entirely with SQLite — see 2d).

docker compose up --build -d

To use the pre-built Docker Hub image instead of building locally, edit docker-compose.yml and swap the build line for the two commented-out lines:

# build: .                          # remove or comment out
image: nullata/webui-manager:latest  # uncomment
pull_policy: always                  # uncomment

Then:

docker compose up -d

2b. Docker Compose - full stack (app + database)

If you want Compose to manage the database as well, extend docker-compose.yml with a MariaDB service and update DB_HOST to match the service name:

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

2c. Plain Docker run

Build the image:

docker build -t webui-manager .

Run the container (supply env vars inline or via --env-file):

docker run -d \
  --name webui-manager \
  --restart unless-stopped \
  --env-file .env \
  -p 5000:5000 \
  webui-manager

Or pull the pre-built image from Docker Hub:

docker run -d \
  --name webui-manager \
  --restart unless-stopped \
  --env-file .env \
  -p 5000:5000 \
  nullata/webui-manager:latest

2d. Docker — self-contained with SQLite

No external database needed. Mount a volume for the SQLite file so it survives container restarts:

docker run -d \
  --name webui-manager \
  --restart unless-stopped \
  -p 5000:5000 \
  -e SECRET_KEY=change-this-secret \
  -e DB_TYPE=sqlite \
  -e DB_PATH=/data/webui_manager.sqlite \
  -v webui-data:/data \
  nullata/webui-manager:latest

Or with Compose: the included docker-compose.yml already contains the SQLite lines commented out — uncomment DB_TYPE, DB_PATH, and the two volumes sections. The result looks like this (the DB_* MySQL vars are ignored when DB_TYPE=sqlite, so they can stay or go):

services:
  app:
    build: .    # or: image: nullata/webui-manager:latest
    restart: unless-stopped
    ports:
      - "${APP_PORT:-5000}:5000"
    environment:
      SECRET_KEY: ${SECRET_KEY}
      APP_CREDENTIALS_KEY: ${APP_CREDENTIALS_KEY:-}
      AUTO_MIGRATE: ${AUTO_MIGRATE:-true}
      DB_TYPE: sqlite
      DB_PATH: /data/webui_manager.sqlite
    volumes:
      - db_data:/data

volumes:
  db_data:
docker compose up -d

First run

Once the container is running, navigate to http://localhost:5000 (or your configured port). Tables are created automatically on startup - follow the on-screen admin setup prompt.

Upgrading

Schema migrations run automatically on startup (AUTO_MIGRATE, on by default): missing tables and columns are created, nullable and column-type changes are applied, and missing indexes, unique constraints, and MyISAM→InnoDB conversions are handled. Upgrading from any earlier version - including pre-service-type deployments - is just a matter of deploying the new version and restarting. Backing up your database before upgrading is still recommended.

The plain SQL scripts in the migrations/ directory are redundant with auto-migration and kept for reference; they are only needed if you run with AUTO_MIGRATE=false and want to apply schema changes by hand.

Development

# Test suite (pytest + Playwright) - see tests/README.md
pip install -r requirements-dev.txt
playwright install chromium          # one-off, for the browser tests
pytest                               # everything
pytest -m "not browser"              # API and database only, no browser needed

# Rebuild the CSS after editing templates, JS, or tailwind-src.css.
# Output (app/static/css/tailwind.css) is committed, so rebuild before pushing.
./build-tailwind.sh

Tests run against a temporary SQLite database and never touch your .env or real database. There is no JavaScript build step - app/static/js/app.js is plain vanilla JS loaded directly.

Third-Party Licenses

Font Awesome Free 7.1.0 is bundled under CC BY 4.0 (icons), SIL OFL 1.1 (fonts), and MIT (code). See app/static/fontawesome-free-7.1.0-web/LICENSE.txt.

IBM Plex Sans and Space Grotesk are bundled under the SIL Open Font License 1.1. See app/static/fonts/IBM_Plex_Sans/OFL.txt and app/static/fonts/Space_Grotesk/OFL.txt.

Credits

The UI was generated with Gemini 2.5 and refined with many manual and agentic adjustments.

License

Licensed under the Apache License 2.0.

webuiception

apps.marketingCta.appInstallTitle

apps.marketingCta.appInstallDescription

apps.installHelp.stepOpen apps.installHelp.stepSearchApp apps.installHelp.stepReview apps.installHelp.stepInstall

apps.detail.sections.categories

apps.detail.sections.related

apps.detail.sections.details

apps.detail.details.repository
nullata/webuimanager
apps.detail.details.lastUpdated2026-08-10
apps.detail.details.firstSeen2026-08-05

apps.detail.sections.runtime

apps.detail.details.webui
http://[IP]:[PORT:5000]
apps.detail.details.network
bridge
apps.detail.details.shell
sh
apps.detail.details.privileged
false

apps.detail.sections.configuration

PortPorttcp
apps.detail.config.target
5000
apps.detail.config.default
5000
apps.detail.config.value
5000
TZVariable

Your UTC TZ offset for database timestamp recording. List of available TZs: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

apps.detail.config.default
Europe/Sofia
apps.detail.config.value
Europe/Sofia
DB_HOSTVariable

Your MariaDB/MySQL database host or container name

apps.detail.config.default
changeme
apps.detail.config.value
changeme
DB_PORTVariable

MariaDB/MySQL database port

apps.detail.config.default
3306
apps.detail.config.value
3306
DB_USERVariable

The designated user for this application in the database. For more info on the requirements, please visit: https://github.com/nullata/webui-manager/blob/main/README.md#database-setup

apps.detail.config.default
webui
apps.detail.config.value
webui
DB_PASSVariable

Your MariaDB/MySQL database user password.

apps.detail.config.default
changeme
apps.detail.config.value
changeme
DB_NAMEVariable

Your MariaDB/MySQL database name. For more info on the requirements, please visit: https://github.com/nullata/webui-manager/blob/main/README.md#database-setup

apps.detail.config.default
webui_manager
apps.detail.config.value
webui_manager
DB_PARAMSVariable

Additional database connection string parameters. Make sure to set a value for charset matching your database. Default: ?charset=utf8mb4

apps.detail.config.default
?charset=utf8mb4
apps.detail.config.value
?charset=utf8mb4
SECRET_KEYVariable

Flask session secret. Required for multi-replica deployments. Leave blank for auto-generated per restart.

apps.detail.config.value
auto
APP_CREDENTIALS_KEYVariable

Key used to encrypt stored credentials at rest. Leave blank for auto-generated per restart.

apps.detail.config.value
auto
SESSION_COOKIE_NAMEVariable

Name of the session cookie. Only change this if you run several copies of WebUI Manager on the same hostname and want each to keep its own login. Default: webui_manager_session

apps.detail.config.default
webui_manager_session
apps.detail.config.value
webui_manager_session
AUTO_MIGRATEVariable

Run automatic schema migration on startup. Default: true

apps.detail.config.default
true
apps.detail.config.value
true