A self-hosted reverse proxy manager for Ubuntu 24.04. FastAPI + SQLite + nginx + Let's Encrypt, with a GitHub-based in-app update system and a dark control-plane UI.
One line, on a fresh Ubuntu 24.04 box (run as root or with sudo):
curl -sSL https://raw.githubusercontent.com/marsh4200/ar-reverse-proxy/main/install.sh | sudo bashThe installer:
- Installs
nginx,certbot,python3-venv,git,ufw - Clones the repo to
/opt/ar-reverse-proxy - Creates a Python venv and installs requirements
- Writes
/etc/ar-reverse-proxy/envwith a freshly generatedSECRET_KEY - Registers and starts the
ar-reverse-proxysystemd service - Opens ports 80, 443, and 9914 via
ufw
When it finishes, the dashboard is at http://<server-ip>:9914 with the default login admin / admin. Change the password from Settings → Change password in the dashboard; the panel warns you until you do. You can also set ARRP_ADMIN_PASS in /etc/ar-reverse-proxy/env before the first start.
The dashboard is a single control plane for every route:
- Route lines — each proxy is drawn as the hop it is: public domain on the left, upstream on the right, with TLS / WebSocket / external-host state on the connector.
- At a glance — total routes, how many are HTTPS-secured, how many pass WebSockets, and live nginx service state.
- Create, edit and delete in place — the Host header override only appears for HTTPS upstreams, where it actually matters.
- Settings drawer — account, password, updates and host details.
- Responsive down to a phone, keyboard accessible, and honours
prefers-reduced-motion.
The UI ships as hand-written CSS with no build step and no CDN dependency, so it renders correctly on an isolated network.
| New route | Settings |
|---|---|
![]() |
![]() |
More, including tablet and mobile layouts, in screenshots/.
- Add a reverse proxy from the dashboard (domain → host:port) and the backend writes
/etc/nginx/sites-available/arrp_<domain>.conf, symlinks it intosites-enabled, runsnginx -t, and reloads nginx. Ifnginx -tfails, the new config is rolled back automatically. - Tick "Auto-SSL" to have
certbot --nginxprovision a Let's Encrypt certificate for the domain (the DNS must already point at the server). - WebSocket support is on by default and adds the
Upgrade/Connectionheaders to the generated config.
Open Settings → Updates and click Check now. The backend fetches VERSION from raw.githubusercontent.com/<repo>/<branch>/VERSION and compares it to the local VERSION file.
If a newer version exists, the banner shows it and Install update triggers scripts/update.sh, which:
- Detaches from the FastAPI process (so
systemctl restartcan't kill it mid-flight). git fetch && git reset --hard origin/<branch>- Re-installs
requirements.txtinto the venv. - Re-installs the systemd unit if it changed.
- Restarts the service.
The dashboard polls /healthz until the new process is up, then reloads itself. Update logs go to /var/log/ar-reverse-proxy-update.log and the update_logs SQLite table.
To cut a release, bump the number in VERSION, commit, and push to main.
ar-reverse-proxy/
├── VERSION # version string compared against GitHub
├── install.sh # one-line installer
├── requirements.txt
├── app/
│ ├── main.py # FastAPI entrypoint
│ ├── config.py # Settings (env-driven)
│ ├── database.py # SQLAlchemy engine/session
│ ├── models.py # User, Proxy, UpdateLog
│ ├── schemas.py # Pydantic I/O models
│ ├── auth.py # JWT + bcrypt + cookie helpers
│ ├── routers/
│ │ ├── auth.py # /login, /logout, /api/login
│ │ ├── proxies.py # /api/proxies CRUD
│ │ └── update.py # /api/update status + run
│ ├── services/
│ │ ├── nginx_service.py # config generation, nginx -t, reload, certbot
│ │ └── update_service.py # GitHub VERSION check + run update.sh
│ ├── templates/ # Jinja2: base, login, dashboard
│ └── static/ # Tailwind CDN + small CSS/JS
├── scripts/
│ └── update.sh # pulled + executed during in-app update
└── systemd/
└── ar-reverse-proxy.service
- FastAPI for the API and HTML rendering
- SQLite via SQLAlchemy at
/var/lib/ar-reverse-proxy/arrp.db - JWT (HS256) in an httpOnly cookie for session auth
- TailwindCSS via CDN (no build step)
- systemd unit running uvicorn on
:9914
| Action | Command |
|---|---|
| Status | systemctl status ar-reverse-proxy |
| Logs | journalctl -u ar-reverse-proxy -f |
| Restart | systemctl restart ar-reverse-proxy |
| Update log | tail -f /var/log/ar-reverse-proxy-update.log |
| Manual update | sudo /opt/ar-reverse-proxy/scripts/update.sh |
| Config | /etc/ar-reverse-proxy/env |
| DB | /var/lib/ar-reverse-proxy/arrp.db |
| Generated nginx | /etc/nginx/sites-available/arrp_*.conf |
- Runs as root because it needs to write nginx config, reload nginx, and invoke certbot. Don't expose port 9914 to the public internet — put the dashboard itself behind an nginx vhost with SSL + IP allow-list, or bind it to
127.0.0.1and tunnel. - The default
admin/admincredentials exist purely for first-run bootstrap. Change them. SECRET_KEYis generated once by the installer and persisted in/etc/ar-reverse-proxy/env. Rotating it invalidates all sessions.


