Skip to content

Repository files navigation

                     /$$                                                    /$$                 /$$
                    |__/                                                   | $$                |__/
 /$$$$$$$   /$$$$$$  /$$ /$$$$$$$  /$$   /$$        /$$  /$$  /$$  /$$$$$$ | $$$$$$$  /$$   /$$ /$$
| $$__  $$ /$$__  $$| $$| $$__  $$|  $$ /$$//$$$$$$| $$ | $$ | $$ /$$__  $$| $$__  $$| $$  | $$| $$
| $$  \ $$| $$  \ $$| $$| $$  \ $$ \  $$$$/|______/| $$ | $$ | $$| $$$$$$$$| $$  \ $$| $$  | $$| $$
| $$  | $$| $$  | $$| $$| $$  | $$  >$$  $$        | $$ | $$ | $$| $$_____/| $$  | $$| $$  | $$| $$
| $$  | $$|  $$$$$$$| $$| $$  | $$ /$$/\  $$       |  $$$$$/$$$$/|  $$$$$$$| $$$$$$$/|  $$$$$$/| $$
|__/  |__/ \____  $$|__/|__/  |__/|__/  \__/        \_____/\___/  \_______/|_______/  \______/ |__/
           /$$  \ $$                                                                               
          |  $$$$$$/                                                                               
           \______/                                                                                

Manage your nginx reverse proxies from a modern web UI

A lightweight, dependency-free web interface for nginx reverse-proxy management — sites, TLS, redirects, config editing, nginx -t, reload/restart, logs and backup, exactly like you do from the shell. Zero rebuilding, no database, ~40 MB RAM.

Python nginx Stack Docker Style License maintained PRs

One command install, or run it from a container. Manages bare-metal nginx or nginx running on the same host from Docker — through a tiny host-side control agent. Web UI frontends with automatically-guessed Let's Encrypt-friendly TLS paths.


Install — one line

Copy-paste this. No cloning, no setup — the installer bootstraps itself, then asks which deployment you want:

curl -fsSL https://raw.githubusercontent.com/himalsimkhada/nginx-webui/main/install.sh | bash

Not ready yet? ./install.sh --check does a safe dry run and reports what the installer would detect on your machine without changing anything.


Table of Contents


Why nginx-webui?

Managing nginx normally means SSH-ing in, hand-writing server blocks that are easy to get wrong, running nginx -t, remembering nginx -s reload, and tailing logs by hand. This project gives you a polished, single-page dashboard for daily reverse-proxy chores — while touching nothing about how nginx runs underneath.

It deals only with the same config files and the same commands real admins use (nginx -t, nginx -s reload, systemctl restart nginx). Your nginx stays yours; the UI just makes it pleasant.


Features

Dashboard Live server state with stat boxes — version, running/master PID, workers, config file — and reload / check / restart buttons. The status card shows which transport answered (host agent / http probe / local /proc).
Reverse-proxy wizard Create a site in one flat form: application name, domain (server_name), upstream (proxy_pass), HTTP and HTTPS ports, websocket upgrade, max upload size, upstream timeout.
TLS made simple Enable TLS and the HTTPS port appears right in the TLS section; pick a saved certificate or type paths manually. Auto-defaults to 443, changeable. Optional Redirect HTTP → HTTPS.
Saved SSL store Named certificate entries reused across sites — stored as file paths or as pasted PEM content (written where both host nginx and the container can read them), with CRUD in a dedicated card.
Sites List, create, read, edit (form or raw config), delete, enable/disable (sites-availablesites-enabled).
Config editor Browse/edit any file under /etc/nginx (path-traversal safe). nginx.conf opens read-only until you click Edit; the nginx-webui .env is editable too.
Validation & control nginx -t, reload (nginx -s reload), restart (systemctl restart nginx) — on the host nginx even when the UI runs in a container.
Backup & restore One-click config tarball; restore validates with nginx -t and rolls back on failure.
Log viewer Tail nginx error/access logs with line-count control and text filtering.
Access protection Shared password (WEBUI_PASSWORD), 30-minute remember me session, log-out button, brute-force lockout (5 failures → 15 min block).
Dark & light mode Theme toggle, persisted in the browser.
Feather-light Flask + vanilla HTML/CSS/JS. No build step, no Node.js, no database — ~40 MB RAM.

Quick start

Install in one command — the installer bootstraps itself when streamed, cloning the repo before it runs:

curl -fsSL https://raw.githubusercontent.com/himalsimkhada/nginx-webui/main/install.sh | bash

Or clone and run directly:

git clone https://github.com/himalsimkhada/nginx-webui.git
cd nginx-webui
./install.sh

You'll be asked which deployment you want:

  1) Docker + host agent   - web UI container managing nginx on THIS machine (recommended)
  2) Manual                - nginx and the web UI both installed directly on THIS machine

Dry run first: ./install.sh --check reports what the installer detects on your machine (distro, nginx, host agent, Docker, python3) without changing a thing.

Dashboard included: the installer detects when the companion webui admin facade (himalsimkhada/webui) is not running and offers to install it for you — the portal hosts the dashboard UI, while this service stays API-only.


Requirements

  • Linux with nginx installed (apt install nginx), nginx -t-clean config
  • Python 3.10+ (manual mode only)
  • sudo access (for config files and nginx control)
  • Docker is optional — needed only for the containerized deployment

Supported distros: Debian/Ubuntu (apt), RHEL/Fedora (dnf), Arch (pacman).


Deployment options

Option What runs where When to pick it
1. Docker + host agent Web UI in a container, nginx on the host You keep your existing nginx, UI stays containerized
2. Manual Everything on this machine, systemd service Minimal footprint, single server

Option 1 — Docker + host control agent

The web-UI container mounts /etc/nginx and /var/log/nginx from the host — config editing, site management and nginx -t work straight from it. But a container lives in another PID namespace and has no systemctl, so it cannot signal or restart the host nginx master. For that, a tiny host-side control agent (host_agent.py, standard library only) runs as a systemd service on the host:

# ON THE NGINX HOST — one time
sudo cp host_agent.py /usr/local/bin/nginx-webui-host-agent.py
sudo chmod 755 /usr/local/bin/nginx-webui-host-agent.py
sudo cp nginx-webui-host-agent.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nginx-webui-host-agent

It binds 0.0.0.0:9401 (so containers reach it via host.docker.internal); restrict with a firewall if the host is on a LAN. API (JSON): GET /status, POST /check, POST /reload, POST /restart. The installer (mode 1) does all of this for you, writes .env, then:

cp .env.example .env
docker compose up -d

.env sets NGINX_CTL_URL=http://host.docker.internal:9401, so status, check, reload and restart all act on the host nginx. The compose file adds extra_hosts: host.docker.internal → host-gateway; if your Docker lacks host-gateway, put the host's LAN IP in NGINX_CTL_URL instead.

Option 2 — Manual (bare-metal)

Everything on this one machine, managed as a systemd service. Web UI at http://localhost:8400.

./install.sh     # choose 2) Manual

By hand:

python3 -m venv venv
./venv/bin/pip install -r requirements.txt
WEBUI_PASSWORD='your-password' SECRET_KEY='a-long-random-string' ./venv/bin/python app.py

As a boot-starting service (running as root, no sudo rules needed):

sudo cp nginx-webui.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nginx-webui

If you prefer to run the service as an unprivileged user, add passwordless rules:

echo 'himal ALL=(ALL) NOPASSWD: /usr/sbin/nginx, /usr/bin/systemctl restart nginx' > /etc/sudoers.d/nginx-webui

Configuration

All container settings live in .env (see .env.example); bare-metal uses environment variables or the systemd EnvironmentFile:

Variable Default Purpose
NGINX_CONF_DIR /etc/nginx nginx config directory (files, sites, SSL store)
NGINX_WEBUI_PORT 8400 API listen port
NGINX_STATUS_URL (empty) HTTP probe of a reachable nginx stub_status for status-only checks
NGINX_CTL_URL (empty) Host control agent URL for status/check/reload/restart of the host nginx (containers)
NGINX_WEBUI_ENV_FILE /etc/nginx-webui.env Where the .env shown in the UI lives
SSL_STORE_FILE $NGINX_CONF_DIR/webui-ssl-store.json Saved certificate store
SSL_CERTS_DIR $NGINX_CONF_DIR/ssl Where pasted PEM content is written
LOG_FILE (auto) Log file to tail when journalctl isn't available (containers)
NGINX_TIMEOUT 10 Command timeout (s) for nginx operations
WEBUI_PASSWORD (empty = auth off) Single shared password required to use the UI
SECRET_KEY (dev default) Secret used to sign the session cookie; set a random value
MAX_LOGIN_ATTEMPTS / LOCK_TIME 5 / 900 Brute-force lockout config

Status source priority: NGINX_CTL_URL (host agent) → NGINX_STATUS_URL (HTTP probe) → local /proc + direct commands (bare-metal).

Access protection: set WEBUI_PASSWORD to require a password at login. The Remember me checkbox persists the session for 30 minutes then auto-logs-out (otherwise the session ends when the browser closes). A Log out button lives in the nav bar. If the variable is empty, authentication is disabled entirely.


The .env in the UI

The file holding NGINX_STATUS_URL / NGINX_CTL_URL / etc. shows up as .env in the dashboard's Config Files editor, so you can see and change it without SSH. Bare metal: /etc/nginx-webui.env (the systemd EnvironmentFile). Docker: ./.env next to docker-compose.yml is mounted there. Edits apply after restarting the service or docker compose up -d.


Security notes

  • Authentication is a single shared password compared against the configured WEBUI_PASSWORD — nothing stored on disk, no user database.
  • Sessions use a signed cookie (set SECRET_KEY!), with brute-force lockout (5 failed logins → 15 min block) on the login form.
  • Config file access is path-traversal safe and confined to NGINX_CONF_DIR (the .env being the single deliberate exception).
  • The host control agent is fire-and-forget: bind 0.0.0.0 only for Docker reachability, restrict with a firewall, and it exposes an intentionally tiny API.
  • nginx.conf opens read-only and requires an explicit Edit click to change.
  • Restore writes go through the same validation gate nginx itself uses (nginx -t), with rollback on failure.
  • No build step, no runtime downloads, no telemetry, no analytics.

API reference

Method Endpoint Description
POST /api/login Login (shared password)
GET /api/session Auth state
GET /api/system Service identity
GET /api/status Version, pid, workers, config file
GET · POST /api/check nginx -t validation
GET · POST /api/control/check Alias of /api/check (same pattern as reload/restart)
POST /api/control/reload nginx -s reload (host agent or local)
POST /api/control/restart systemctl restart nginx (host agent or local)
GET /api/config/files Editable files
GET · PUT /api/config/file/<path> Read / write a config file
GET /api/sites Site list (enabled + available)
GET · PUT · DELETE /api/site/<name> Site CRUD
POST /api/site Create reverse-proxy site
POST /api/site/<name>/toggle Enable / disable site
GET /api/ssl List saved certificates
POST /api/ssl Add a certificate (paths or PEM)
PUT · DELETE /api/ssl/<name> Update / delete a certificate
GET /api/logs?lines=&query= Error/Access log tail
GET /api/backup Config tarball (base64)
POST /api/restore Apply + nginx -t + rollback
GET /healthz /readyz Probes (open)
GET /metrics Prometheus metrics (auth-gated)
GET /api/metrics/portal Gauges as JSON

Development

python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt

pytest                      # full suite, no nginx required (binary faked)

The suite covers status + status-source priority, config editing (nginx.conf, sites, .env), reverse-proxy site create/edit with separate HTTP/HTTPS ports, SSL store with both file-path and pasted-PEM modes, backup/restore, the control agent path, auth + lockout, and the API. CI runs on GitHub Actions for every push and PR.


Project structure

nginx-webui/
├── app.py                  # Flask backend — JSON API only
├── nginx_manager.py        # status, control, sites, SSL store, config, backup
├── host_agent.py           # host-side nginx control agent (stdlib only)
├── metrics.py              # /metrics, /healthz, /readyz (prometheus text)
├── templates/              # — (none: this service is API-only)
├── tests/                  # pytest suite
├── requirements.txt        # flask
├── Dockerfile              # Container image
├── docker-compose.yml      # Web UI container + host mounts + host.docker.internal
├── install.sh              # bootstrapping one-shot installer (--check safe)
├── nginx-webui.service     # Systemd unit (bare-metal)
├── nginx-webui-host-agent.service  # Systemd unit for the host control agent
└── .env.example            # Container configuration template

The web-UI frontend lives in the companion repo himalsimkhada/webui — the admin facade that talks to nginx-webui (and BIND) over HTTP.


How it works

The backend drives nginx through the exact same tools an admin does:

  • Controlnginx -t, nginx -s reload, systemctl restart nginx; delegated to the host control agent when running in a container (PID namespaces block direct signals).
  • Status — host agent /status, an HTTP stub_status probe, or local /proc + nginx -v / nginx -T on bare metal; the dashboard shows which.
  • Config files — reads/writes nginx.conf, conf.d/, snippets/, sites-available/ under /etc/nginx (or your NGINX_CONF_DIR).
  • Sites — generated server blocks include TLS (server_name, listen 443 ssl, cert/key), optional HTTP→HTTPS redirect, websocket Upgrade headers, and client_max_body_size / proxy_read_timeout.
  • Validation — every saved server block passes nginx -t before the reload is offered.
  • Logs — mounted log files (containers) or journalctl (bare metal).

Transport is chosen from the environment: as a systemd service it acts directly on the host nginx; inside a container it talks to the host through NGINX_CTL_URL. No database. No magic.


Poke around, file an issue, open a PR — feedback welcome.

License

MIT

About

Lightweight, dependency-free web UI to manage nginx reverse proxies: sites, TLS, config editor, nginx -t, reload/restart, logs. Flask + Docker, no database.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages