Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
To install Mezzanine on Ubuntu and run it as a production site, use the same layered design as a conventional Django deployment: a pinned Python/Mezzanine/Django combination, a dedicated Unix account and virtual environment, PostgreSQL, Gunicorn, Nginx, and HTTPS. Mezzanine’s mezzanine-project command creates the project; Django migrations create the schema; Gunicorn runs the WSGI application; and Nginx serves static files and accepts public HTTP(S) traffic.
The sequence below keeps the quick-start workflow separate from production hardening. You will first provision the VPS and verify the application, then replace SQLite with PostgreSQL, configure production settings, run Gunicorn under systemd, place Nginx in front, and add backups and certificate renewal.
Free tools Windows power users keep installed
One-click scans. No signup required.
Choose a compatible Mezzanine, Django, Python, and Ubuntu combination
Do not install whatever version the Ubuntu image happens to call python3 and then add the newest Mezzanine package. Mezzanine’s supported interpreter and Django ranges changed between documentation eras, so pin all three components before you create the virtual environment. The following ranges are the ones published for the cited releases and repository overview; they are compatibility metadata, not a promise that every combination inside a range works with every Ubuntu release.
| Mezzanine documentation context | Supported Python versions | Supported Django versions | How to use the information |
|---|---|---|---|
| Mezzanine 4.3.1 overview | 2.7–3.6 | 1.8–1.11 | Use only with an Ubuntu/Python image that can provide this older interpreter stack. |
| Repository overview for newer releases | 3.7–3.10 | 2.2–4.0 | Pin a release and interpreter from this newer compatibility family rather than mixing it with the 4.3.1-era requirements. |
Write down the exact Ubuntu LTS image, Mezzanine release, Django release, Python interpreter, PostgreSQL major version, and database adapter you intend to use. If the chosen Mezzanine release does not declare support for the interpreter supplied by the image, select a different image or release. Keep the resulting versions in requirements.txt so a later deployment cannot silently resolve different packages.
#1 Best Overall
Prepare the Ubuntu VPS
Use an existing administrative account for system changes and keep the application outside /root. A separate deployment account limits the damage from a compromised application process.
-
Create a normal account without a login shell for routine administration if your operational policy allows it:
Recommended Free Tools
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.sudo adduser --disabled-password --gecos '' mezzanine -
Create an application directory owned by that account:
sudo install -d -o mezzanine -g mezzanine /srv/mezzanine -
Update the operating system and install security updates:
sudo apt update sudo apt full-upgrade -
Allow only the ports needed for administration and web traffic. With UFW, enable SSH before enabling the firewall so you do not lock yourself out:
sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo ufw status verbose
Restrict SSH separately with keys, disable password authentication after testing key access, and use your provider’s console as a recovery path. The deployment account should not own unrelated services or have write access to system configuration.
Install system prerequisites
Package names depend on the Ubuntu release and the Python version selected in the compatibility step. On a current Ubuntu image using the distribution Python 3 toolchain, the following is a practical starting point:
sudo apt install python3-venv python3-dev build-essential libpq-dev git postgresql nginx
If you selected an older Python required by an older Mezzanine release, install that interpreter and its matching virtual-environment package instead of substituting the command above. build-essential and the Python development headers allow packages such as Pillow to compile; libpq-dev supplies headers for PostgreSQL adapters. Install the adapter version that supports your pinned Python and Django versions.
PostgreSQL and Nginx can be installed on the same VPS for a small site. A managed PostgreSQL service is also valid, but you must still provide a reachable database, credentials, network rules, backups, and a restore procedure.
Create the virtual environment and pin packages
Switch to the deployment account and create one virtual environment dedicated to this project. The environment keeps Mezzanine’s dependencies independent from Ubuntu’s system Python.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →sudo -iu mezzanine
cd /srv/mezzanine
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install 'mezzanine==MEZZANINE_VERSION' 'Django==DJANGO_VERSION' gunicorn
Replace the uppercase tokens with versions that Mezzanine explicitly supports. If your selected database adapter is not installed as a Mezzanine dependency, install its compatible package at this point as well. Verify the resolver did not replace your chosen Django version:
Rank #2
python -m pip show mezzanine Django gunicorn
python -m pip freeze > requirements.txt
Commit requirements.txt with the application code. On subsequent servers, use python -m pip install -r requirements.txt rather than resolving an unpinned set of packages.
Scaffold the Mezzanine project
Run the project generator from the application directory. In this example the Python package and WSGI module will be named site.
mezzanine-project site
cd /srv/mezzanine/site
The generator creates manage.py, a project package, URL configuration, settings, and a WSGI entry point. Keep the project package name consistent with the Gunicorn target used later: site.wsgi:application.
Use the quick-start database command only for an initial check
Mezzanine’s custom createdb command wraps Django migrations and can load demonstration content:
python manage.py createdb --noinput
For a clean installation without demo pages, add the documented --nodata option:
python manage.py createdb --noinput --nodata
This quick-start path uses the generated default database configuration, normally SQLite. It is useful for confirming that the project starts, but do not leave a public production site on SQLite simply because the command succeeded. Configure PostgreSQL before the production migration described below.
Create an administrator and perform a local smoke test
If you used --nodata, create an administrator explicitly:
python manage.py createsuperuser
Run the development server only on the loopback interface:
python manage.py runserver 127.0.0.1:8000
Open an SSH tunnel from your workstation if you need to view it remotely. Do not bind runserver to a public address; it is a development server and is not intended to handle production traffic.
Configure PostgreSQL for Mezzanine
Django supports PostgreSQL, MariaDB, MySQL, SQLite, and Oracle, but PostgreSQL is the documented production choice for this stack. Create a database role that owns only the application database and does not have superuser privileges.
sudo -u postgres createuser --no-createdb --no-superuser --no-createrole --pwprompt mezzanine_db
sudo -u postgres createdb --owner=mezzanine_db mezzanine
The first command asks for the role password without placing it in shell history. Choose a long, unique password. If PostgreSQL runs on another host, create the role there and allow only the VPS address through the database firewall and pg_hba.conf.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteKeep database credentials outside the repository
Create a root-readable environment file. Do not commit it, include it in a deployment archive, or print it in diagnostic output:
sudo install -d -m 0750 -o root -g mezzanine /etc/mezzanine
sudo sh -c 'umask 077; install -m 0640 -o root -g mezzanine /dev/null /etc/mezzanine/mezzanine.env'
Put values such as these in the file, changing the host, database name, role, and domain to your installation:
DJANGO_SECRET_KEY=GENERATE_A_LONG_RANDOM_VALUE
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=example.com,www.example.com
DB_NAME=mezzanine
DB_USER=mezzanine_db
DB_PASSWORD=USE_THE_POSTGRESQL_ROLE_PASSWORD
DB_HOST=127.0.0.1
DB_PORT=5432
Make the file readable by the deployment service but not by ordinary users. If you use a managed database, replace DB_HOST and DB_PORT and require TLS according to that service’s instructions.
Set production Django and Mezzanine settings
Edit the generated settings module and load secrets and deployment-specific values from the environment. The exact surrounding file differs by Mezzanine release, so merge the following pattern into the generated settings rather than replacing the whole file:
import os
DEBUG = os.environ.get('DJANGO_DEBUG', 'False').lower() == 'true'
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
ALLOWED_HOSTS = [h.strip() for h in os.environ['DJANGO_ALLOWED_HOSTS'].split(',') if h.strip()]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ.get('DB_HOST', '127.0.0.1'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
SITE_URL = 'https://example.com'
STATIC_ROOT = '/srv/mezzanine/site/staticfiles'
MEDIA_ROOT = '/srv/mezzanine/site/media'
Keep the generated STATIC_URL and MEDIA_URL values aligned with the Nginx locations you will configure, normally /static/ and /media/. Set SITE_URL to the canonical HTTPS origin used in links and feeds. Include every hostname that should answer in ALLOWED_HOSTS, and do not use a wildcard on an Internet-facing site.
Generate the secret key with a cryptographically secure method, store it only in the environment file or a secrets manager, and never rotate it casually: changing it invalidates signed sessions and other data protected by the key. Set DEBUG = False for the public service. When using HTTPS behind Nginx, configure the forwarded-protocol handling expected by your pinned Django version before enabling any secure-redirect setting; otherwise Django can misread every request as HTTP.
Run migrations and collect static assets
Load the environment file for an interactive migration, or export the same variables in your shell. The commands below assume the file is readable by the deployment account:
set -a
. /etc/mezzanine/mezzanine.env
set +a
cd /srv/mezzanine/site
. /srv/mezzanine/.venv/bin/activate
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py check --deploy
migrate creates Mezzanine and Django tables in PostgreSQL. collectstatic copies package and project assets into STATIC_ROOT, which Nginx will serve directly. Uploaded files belong under MEDIA_ROOT; they are not recreated by collectstatic and must be included in backups.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Read every warning from check --deploy. It commonly identifies debug mode, weak or missing secret configuration, insecure host settings, and missing HTTPS-related protections. Resolve warnings that apply to your chosen Django release before announcing the site.
Test Gunicorn before creating a service
Gunicorn should import the same WSGI module that the generated project created. From the activated virtual environment, start a temporary loopback listener:
cd /srv/mezzanine/site
. /srv/mezzanine/.venv/bin/activate
gunicorn --bind 127.0.0.1:8000 site.wsgi:application
In a second SSH session, request the endpoint:
curl -I http://127.0.0.1:8000/
A response proves that Gunicorn can import the project and reach PostgreSQL. Stop the foreground process with Ctrl-C after the check. If the import fails, verify the working directory, project package name, virtual-environment path, and installed requirements before moving to systemd.
Rank #4
Run Gunicorn under systemd
A system service supplies the working directory, environment, restart policy, and a stable Unix socket. Create /etc/systemd/system/mezzanine.service as root:
[Unit]
Description=Gunicorn service for Mezzanine
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=mezzanine
Group=www-data
WorkingDirectory=/srv/mezzanine/site
EnvironmentFile=/etc/mezzanine/mezzanine.env
RuntimeDirectory=mezzanine
RuntimeDirectoryMode=0750
UMask=007
ExecStart=/srv/mezzanine/.venv/bin/gunicorn --workers 3 --bind unix:/run/mezzanine/gunicorn.sock site.wsgi:application
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
The worker count is an example, not a universal performance recommendation. Increase it only after observing memory use and request latency on your VPS. The service runs as mezzanine; the www-data group lets Nginx access the socket. If your distribution’s Nginx user or your directory permissions differ, use a dedicated shared group and verify access without granting write permission to application code.
Load and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now mezzanine
sudo systemctl status mezzanine
sudo journalctl -u mezzanine -n 100 --no-pager
Confirm that the socket exists and that Gunicorn answers locally through it before configuring Nginx. A failed service normally leaves the reason in journalctl; the most common causes are a wrong WSGI module, a missing environment variable, a database connection failure, or a permission error in the working directory.
Put Nginx in front of Mezzanine
Nginx is the public-facing server. It terminates TLS, serves immutable static files efficiently, handles uploaded media paths, and proxies dynamic requests to Gunicorn over the private Unix socket.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchCreate /etc/nginx/sites-available/mezzanine and replace the example hostnames and paths:
server {
listen 80;
server_name example.com www.example.com;
client_max_body_size 20m;
location /static/ {
alias /srv/mezzanine/site/staticfiles/;
}
location /media/ {
alias /srv/mezzanine/site/media/;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/run/mezzanine/gunicorn.sock;
}
}
The trailing slash on each alias matters: it maps the URL prefix to the corresponding directory. Ensure Nginx can traverse /srv/mezzanine and read the static and media directories without giving it write access to Python source or settings.
Enable the site, remove any conflicting default server, test the syntax, and reload:
sudo ln -s /etc/nginx/sites-available/mezzanine /etc/nginx/sites-enabled/mezzanine
sudo nginx -t
sudo systemctl reload nginx
Visit the domain or use curl with the correct Host header. A successful Nginx response with a Gunicorn error usually means the proxy is working but the application is not; check the systemd journal rather than changing Nginx blindly.
Add HTTPS and canonical host handling
Obtain a certificate for every production hostname, using an ACME client supported by your Ubuntu release or your certificate provider. Configure the client to install the certificate, redirect port 80 to HTTPS, and renew automatically. Test renewal in the client’s dry-run mode and keep port 80 available if the selected challenge requires it.
After HTTPS works, set the canonical SITE_URL to the HTTPS origin and ensure the proxy headers in the server block match Django’s secure-proxy configuration. Redirect only one canonical hostname, such as example.com or www.example.com, to avoid duplicate URLs. Re-run python manage.py check --deploy after changing security settings.
Back up PostgreSQL and uploaded media
A VPS disk failure, accidental deletion, or bad migration can affect both the database and media. Back up them together on a schedule and copy backups off the VPS.
Best Value
sudo -u postgres pg_dump --format=custom mezzanine > /var/backups/mezzanine-$(date +%F).dump
sudo tar -C /srv/mezzanine/site -czf /var/backups/mezzanine-media-$(date +%F).tar.gz media
Protect the backup directory, define a retention policy, and periodically restore a dump and media archive to a separate test location. A backup that has never been restored is only an assumption. Record the PostgreSQL version, environment variables needed to start the application, systemd unit, Nginx configuration, and certificate renewal method so another administrator can rebuild the VPS.
Deploy updates and keep a rollback path
-
Fetch or check out the intended application revision, keeping the previous revision available.
-
Activate the project virtual environment and install the pinned requirements:
. /srv/mezzanine/.venv/bin/activate python -m pip install -r /srv/mezzanine/site/requirements.txt -
Load the production environment and apply migrations:
set -a . /etc/mezzanine/mezzanine.env set +a cd /srv/mezzanine/site python manage.py migrate python manage.py collectstatic --noinput python manage.py check --deploy -
Restart Gunicorn and verify the service, logs, home page, administrator login, static assets, and a media upload:
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.sudo systemctl restart mezzanine sudo systemctl status mezzanine sudo journalctl -u mezzanine -n 100 --no-pager
Before a migration that changes data, take a fresh PostgreSQL dump. If the new release fails, stop traffic if necessary, restore the previous code and requirements, and use a tested database restore plan. Do not assume that every migration is reversible; review migration files and keep the database backup until the release has been accepted.
Troubleshoot the common failures
ModuleNotFoundError or Gunicorn exits immediately
Check that ExecStart points to /srv/mezzanine/.venv/bin/gunicorn, that WorkingDirectory is the directory containing manage.py, and that the WSGI target matches the generated project package. Run the same command manually as the mezzanine user to expose import errors. Install requirements into that virtual environment, not into system Python.
Nginx returns 502 Bad Gateway
Run sudo systemctl status mezzanine and inspect journalctl. If the service is stopped, fix its Python, settings, or database error first. If it is running, verify that /run/mezzanine/gunicorn.sock exists, that its group permits Nginx to connect, and that the Nginx proxy_pass path exactly matches the systemd bind path. Reload Nginx only after nginx -t succeeds.
Static files return 404 or the browser shows unstyled pages
Confirm that STATIC_ROOT is the same directory passed to Nginx’s alias, run collectstatic --noinput with production settings, and check that Nginx can traverse and read the directory. Do not point Nginx at the source tree’s individual package directories; the collected directory is the production source for static assets.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Django reports “DisallowedHost”
Add the exact requested hostname to the comma-separated DJANGO_ALLOWED_HOSTS value, including both apex and www names if both are configured. Reload Gunicorn after changing the environment file. Do not solve the error with * on a public server.
PostgreSQL authentication or connection errors
Verify the database name, role, password, host, and port in the environment file; confirm that the role owns the database; and test connectivity with the PostgreSQL client using the same host and role. For a remote database, check firewall rules and TLS requirements. A successful SQLite quick start does not prove that the PostgreSQL settings are valid.
Uploads disappear after a redeploy
Keep MEDIA_ROOT on a persistent directory that is not deleted when application code is replaced. Confirm the Nginx /media/ alias and include that directory in every backup. Static files can be regenerated; user uploads generally cannot.
The selected release will not install on the Ubuntu Python version
Stop and return to the compatibility matrix. Do not force installation with dependency overrides. Choose an Ubuntu image that supplies the interpreter declared by the Mezzanine release, or choose a Mezzanine/Django pair that supports the interpreter you intend to operate, then rebuild the virtual environment and regenerate the pinned requirements.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Production verification checklist
- The Ubuntu image, Python interpreter, Mezzanine release, Django release, and adapter are explicitly pinned.
- The application runs as the dedicated
mezzanineaccount rather than root. - PostgreSQL is used for the live site, with a least-privilege role and a secret stored outside the repository.
DEBUGis false,ALLOWED_HOSTScontains only real hostnames, andSECRET_KEYis long and private.migrate,collectstatic --noinput, andcheck --deploycomplete without unresolved warnings.- Gunicorn is supervised by systemd and listens on a private Unix socket.
- Nginx serves
/static/, maps persistent/media/, forwards the correct host and protocol headers, and passesnginx -t. - HTTPS works, HTTP redirects to the canonical host, and certificate renewal has been tested.
- PostgreSQL dumps and media archives are automated, stored off the VPS, and restorable.
- The update procedure identifies a previous code revision and includes a database rollback or restore plan.
What the finished architecture provides
At the end of this process, the browser reaches Nginx on ports 80 and 443. Nginx serves collected static files and persistent media, then forwards dynamic requests through a Unix socket to Gunicorn. Gunicorn imports the Mezzanine WSGI application, which reads secrets from the service environment and stores content in PostgreSQL. Systemd restarts the application after a failure, while backups and pinned requirements provide a controlled recovery path.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

