Table of Contents
- Troubleshooting
- Which version am I running?
- Reading the log
- "No PDF backend is usable"
- "The weasyprint PDF backend is configured but not usable"
- There is no sign-up form
- "This invitation may only be used with the address it was sent to"
- The admin returns 404
- CSRF verification failed
- The pages have no styling
- Downloads give "File not found"
- "The site refused the request (403)"
- "Nothing resembling a job posting was found on that page"
- "That address is on a private or local network"
- "This site's robots.txt asks automated clients not to fetch that page"
- An API token stopped working
- Times are wrong
- Verification or password reset emails never arrive
- I cannot get past the code prompt
- Single sign-on sends me back with an error
- Checking a deployment
- Is it running?
- Something else
Troubleshooting
Which version am I running?
The page footer says Postulo X.Y.Z; Server settings → Overview says it in full beside the
Python and Django versions; and GET /healthz returns it as version, which is the one to
quote in a bug report and the one monitoring can watch to see an upgrade happen.
Reading the log
Server settings → Logs shows what the instance has been saying, newest first, filtered by level, by which part of Postulo said it, or by a word in the message. It is usually the quickest answer to "the notification did not arrive" or "the document was not filed": the failure is recorded with the connection it happened to and the reason the other end gave.
Everything on that page is also on the console, so docker logs postulo gives you the same
records if you have a terminal to hand. Every line written while answering a request ends
with [request <id>], the id the response carried in X-Request-ID and gunicorn's access
line ends with; search the log for it and you have everything that one request said.
Set POSTULO_LOG_FORMAT=json if something parses your console rather than a person
reading it.
Two things worth knowing. Records name people, companies and applications in the course of explaining what failed, so treat the page as you would the rest of the instance before pasting any of it into a bug report. And the file is rotated by size — about five megabytes in total by default — so it cannot fill a disk, which also means a problem from last month may have scrolled off.
If you keep logs centrally, the same records can be served at /logs for a collector to
scrape. It is off by default and needs a token; see Hardening
before turning it on.
If you keep graphs, /metrics serves Prometheus metrics — what exists, what is waiting,
what has failed, and whether the database and migrations are healthy. Also off by default,
and it carries nothing about anybody.
"No PDF backend is usable"
WeasyPrint is installed with Postulo, so this almost always means its system libraries are missing rather than the package:
sudo apt install libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 # Debian and Ubuntu
On Windows those libraries are impractical; use the fallback renderer instead:
uv sync --extra chromium
uv run playwright install chromium
If you have installed one and still see the message, check POSTULO_PDF_BACKEND — when
it names a specific backend, only that one is tried and no fallback happens.
Everything except export works without any renderer.
"The weasyprint PDF backend is configured but not usable"
POSTULO_PDF_BACKEND=weasyprint is set explicitly, and Pango is missing. Either install
the libraries above, or set the value to auto so that Postulo can fall back.
There is no sign-up form
The instance is invite-only, which is the default. Either get an invitation from a staff
member, or set POSTULO_REGISTRATION_OPEN=true. See Accounts and
invitations.
"This invitation may only be used with the address it was sent to"
The invitation names an email address and you are signing up with a different one. Use the address it was issued for, or ask for an unbound invitation.
The admin returns 404
Most likely it is not running: POSTULO_ADMIN_URL is empty by default since 0.3.0, and
an empty value mounts no admin at all. Set it to a path of your choosing to turn it on, and
restart. If it is set, that path is where the admin is — /admin/ will 404 unless that is
what you chose. Server settings covers people, sign-in policy, plugins, email, logs and
defaults without it.
CSRF verification failed
Almost always a reverse proxy. Set POSTULO_CSRF_TRUSTED_ORIGINS to your full origin
including the scheme:
POSTULO_CSRF_TRUSTED_ORIGINS=https://postulo.example.org
Make sure the proxy forwards X-Forwarded-Proto.
The pages have no styling
collectstatic has not run, or POSTULO_STATIC_ROOT points somewhere the application
cannot read:
DJANGO_SETTINGS_MODULE=postulo.config.settings.prod uv run manage.py collectstatic --noinput
Downloads give "File not found"
The database has a record whose file is missing from MEDIA_ROOT. Usually the media
directory was not restored alongside the database — see Backups and your
data.
"The site refused the request (403)"
Bot protection, not a mistake on your part. The page is visible to your browser and refused to your server.
Paste the page source instead: on the capture page, open The site refuses Postulo, or the posting needs a login, view the source in your browser, and paste it in. Postulo fetches nothing when you do. See Capturing postings.
"Nothing resembling a job posting was found on that page"
The page carries no structured data and no usable title. Postings behind a login, or built entirely by JavaScript after the page loads, cannot be read from the outside — Postulo fetches HTML, it does not run a browser.
Use Record an application and paste the text in. If it is a site you use often, a plugin can be written for it: see Writing a plugin.
"That address is on a private or local network"
Capture refuses anything resolving to a loopback, private or link-local address, and there is no setting to permit it. Paste the posting text in by hand.
"This site's robots.txt asks automated clients not to fetch that page"
The site has declined. You can set POSTULO_CAPTURE_IGNORE_ROBOTS=true, which makes you
responsible for the requests your instance makes, or copy the text in by hand.
An API token stopped working
Tokens return 401 when missing, mistyped, revoked, or belonging to a disabled account.
Check the token list under Settings → API tokens: it shows which are revoked
and when each was last used. A lost token cannot be recovered, only replaced.
Times are wrong
Set POSTULO_TIME_ZONE for the instance, and check the time zone on your own profile,
which overrides it. Times are stored in UTC and displayed in your zone; the stored data is
not wrong, only its presentation.
Verification or password reset emails never arrive
Postulo only sends email through the account system, and only if it is configured. See
Configuration. Until it is, nobody who signs up can complete the
verification that sign-in requires — except the account made with createsuperuser,
whose address is trusted, and people who arrived through an invitation bound to their
address.
To verify an address by hand, open the admin (Accounts → Email addresses), find it, and tick Verified. To change a password without email:
uv run manage.py changepassword alex.morgan # the username
I cannot get past the code prompt
Two-factor authentication is on for the account and the phone with the app is gone. Use one of the recovery codes shown when it was set up. Without those, someone with a shell on the server removes the second factor:
uv run manage.py mfa_reset alex.morgan
Then sign in with the password alone and set it up again.
Single sign-on sends me back with an error
Nine times out of ten the redirect URI the identity provider has on file is not the one Postulo sent. Compare the callback shown under Server settings → Sign-in with the provider's application settings character for character: scheme, host, port, trailing slash. A test instance reached on plain HTTP inside a mesh needs the provider to accept a plain-HTTP redirect for it.
If the provider is reached but Postulo refuses the address as unverified, the provider is
not sending email_verified: true in its claims; most can be told to. Until then the
person receives a verification link as anyone else would.
Checking a deployment
This reports anything unsafe about your production configuration:
DJANGO_SETTINGS_MODULE=postulo.config.settings.prod uv run manage.py check --deploy
Is it running?
/healthz returns JSON and checks the database connection. Useful for uptime monitoring,
and it is what the container's own health check asks — so a container marked unhealthy
means that endpoint answered 503 or did not answer at all.
It is exempt from the HTTPS redirect, and has to be: the check is curl inside the
container talking to 127.0.0.1, where there is no TLS to be redirected to. Before v0.3.0
it was not exempt, so the probe received a 301 — and curl -f treats a redirect as
success. Every container running 0.2.x reports healthy whatever is wrong with it,
including a database that has gone away. If you are on 0.2.x and want a working probe
before upgrading, set POSTULO_SSL_REDIRECT=false and let your reverse proxy do the
redirecting, which is what most of them do anyway.
/metrics is exempt for the same reason. /logs is not: it carries entries naming
connections, companies and applications, so a collector must reach it over HTTPS through
the proxy rather than over plain HTTP inside the network.
Something else
Open an issue on the repository,
with the version (git rev-parse --short HEAD), what you did, and what happened. Please
do not paste your .env.
Postulo
Running it
- Installing Postulo
- Configuration
- Accounts and invitations
- Backups and your data
- Hardening
- Accessibility
- Health, metrics and logs
- Troubleshooting
Using it
- Getting started
- Listings
- Tracking applications
- Capturing postings
- Insights and the dashboard
- Reports
- Your career record
- CVs and portfolios
- Letters
- Files and what you sent
Building on it
Project