Sites and applications

Python sites

A Python site runs your WSGI application under gunicorn as a managed systemd service behind nginx with HTTPS. You bring the code and its dependencies; the panel runs and supervises it.

Verified against auraPanel 1.21.3· Last updated 17 September 2026

How a Python site runs

A Python site is a WSGI application served by gunicorn as a systemd unit, aurapanel-site-<domain>.service, running as the site's Linux user on a loopback port. nginx proxies the domain to that port with HTTPS. The unit runs, in effect:

/usr/bin/gunicorn --chdir /home/<user>/htdocs/<domain> --bind 127.0.0.1:<port> main:app

The panel runs and supervises the process. It does not create a virtual environment, install requirements or detect your entry point: you supply the module and install the dependencies. This keeps the panel out of your Python tooling choices.

Prerequisites

  • Python 3 installed under Services → Packages (the panel installs python3, python3-venv and python3-pip), or with --python=yes at install time.
  • gunicorn at /usr/bin/gunicorn: sudo apt install gunicorn. The unit refuses to start without it and says so.
  • A WSGI application object, for example app in main.py (Flask, Django's wsgi.application, Bottle, Falcon).

Create the site and deploy

  1. Sites → Add site → Python. Enter the domain, accept the suggested app port, and set the app module in gunicorn's module:callable form. The default is main:app.
  2. Create. The unit exists but has nothing to run until your code is in place.
  3. Deploy into the document root and install dependencies as the site user. A virtual environment inside the document root is the simplest layout; the system gunicorn will import your app with the interpreter it was installed for, so install packages for the same Python, or point gunicorn at your venv from a wrapper if you need isolation:
sudo -u <site-user> -i
cd ~/htdocs/example.com
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
exit
sudo systemctl restart aurapanel-site-example.com

gunicorn is the distribution package running under the system Python. Packages installed only inside .venv are visible to it only if you make them so (for example a .pth file, or installing them system-wide for the site user with pip install --user). Check the journal after the first restart.

Environment your app receives

  • PORT and HOST=127.0.0.1: gunicorn is already bound for you; these are informational.
  • TZ when the site has a time zone.
  • Your own variables from a .env file in the document root, loaded before the panel's.
  • REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_KEY_PREFIX when requirements.txt, pyproject.toml or Pipfile declares a Redis client and Redis is installed.
Expected result

The domain serves your application over HTTPS and journalctl -u aurapanel-site-example.com shows gunicorn's workers booting.

Restarts and logs

Same policy as Node.js sites: Restart=always with back-off, parked as failed after five failures in a minute, 10 seconds to stop. Logs go to the journal and the site's Logs tab; nginx logs are in /home/<user>/logs/.

Common messages

"gunicorn is not installed at /usr/bin/gunicorn — install it (e.g. apt install gunicorn) before deploying the Python site …"

Install the package and restart the unit.

"dependency install is only for Node.js sites"

The Install dependencies action does not apply to Python. Install with pip as shown above.

ModuleNotFoundError in the journal

gunicorn cannot import your module: the app module field is wrong, or the dependency is installed for a different interpreter than the system gunicorn uses.

Try the live demo

A real auraPanel on a real server, shared with everyone. Sign in with the demo account and click around.

Email
demo@goaura.one
Password
demo1234
Open the demo panel ↗

Shared public demo, so please don’t put anything personal in it. More about the demo →

Talk to us

Tell us what you need and how to reach you. A person reads this, not a queue.

We use these details only to reply to you. Privacy

Recent updates

What shipped in the last releases. With automatic updates on, each lands on your server the moment it is published.

  1. v1.21.3
    Framework sites were undetectable, and a restore could silently point the copy at the original's live database.

    DetectApp looked for each application's signature relative to the docroot — but every PHP framework in the registry serves from a subdirectory (DocrootSub: public, webroot, web, pub, docroot) and keeps the file that identifies it one level *above* that. So the Laravel signature searched <project>/public/artisan,…

  2. v1.21.2
    Restarting nginx now blocks the panel and reconnects, instead of showing a notice.

    v1.21.1 stopped the false "HTTP 520" but only put up a toast — and a toast is the wrong affordance when the panel itself is about to go away: the page sat there looking broken while every request failed underneath it.

  3. v1.21.1
    Restarting nginx from Services no longer reports a false failure.

    It showed "Restart failed — HTTP 520". The panel is reverse-proxied *by* nginx, so the request deadlocked against itself: the handler blocked waiting for the restart to finish, while nginx's graceful stop waited for that very request to drain. Neither could proceed until nginx's stop-retry timeout force-killed it,…

  4. v1.21.0
    Per-site X-Frame-Options, fixing page builders that couldn't load their preview.

    Elementor's editor failed with *"Multiple 'X-Frame-Options' headers with conflicting values ('DENY, SAMEORIGIN')"*. The cause is that nginx's add_header appends rather than replaces: when the application also sets the header — a WordPress security plugin, or a CDN transform — the browser received two values,…

  5. v1.20.6
    The multi-database restore refusal now says what is in the backup and what still works.

    It read only "Restoring a multi-database site isn't supported yet", which is a dead end: it named neither the databases involved, nor why one of them is the limit, nor any route forward.

  6. v1.20.5
    Services: cards in a row now match heights.

    Like the settings grid before it, the Services grid pinned each card to its own content height, so a row often showed one card noticeably shorter than the card beside it. Each row now takes the taller card's height and both fill it, and it re-balances as content changes — a runtime installed, a longer service list.

  7. v1.20.4
    Settings: paired cards now match heights.

    Organisation / Panel Domain and auraPanel Updates / Session timeout each sat at their own content height, so a row showed one card visibly shorter than its neighbour. Each row now takes the taller card's height and both fill it — driven by content, not a fixed value, so it still adapts as a card grows (a validation…

  8. v1.20.3
    Settings: Organisation and Panel Domain now share a row too.

    They were full-width blocks stacked above the rest of the page; they now sit side by side in the same two-column grid as the Updates / Session timeout pair, so the top of Settings reads as two tidy rows instead of four stacked bands. Both cards shrink cleanly — the logo drop zone is a fixed 44×44 and the name field…

All releases on GitHub ↗