Log File Locations
| Log | Path | What it contains |
|---|---|---|
| Gunicorn errors | /srv/SecBoard/logs/gunicorn_error.log | Python tracebacks, SMTP errors, unhandled exceptions |
| Gunicorn access | /srv/SecBoard/logs/gunicorn_access.log | Every HTTP request: status code, response time, IP |
| Celery worker | /srv/SecBoard/logs/celery_worker.log | Background task execution, failures, retries |
| Celery beat | /srv/SecBoard/logs/celery_beat.log | Scheduled task triggering |
| Nginx errors | /var/log/nginx/error.log | Upstream connection failures, TLS errors, config errors |
| Nginx access | /var/log/nginx/access.log | All requests reaching Nginx (before proxying) |
| systemd journal | journalctl -u secboard -n 100 | Service start/stop events, crash restarts |
The fastest way to see recent errors: tail -f /srv/SecBoard/logs/gunicorn_error.log
while reproducing the problem in the browser.
HTTP Errors
Cause: Django's CSRF middleware rejected the request because the origin is not in CSRF_TRUSTED_ORIGINS, or because a stale/missing CSRF cookie was submitted.
Fix:
- Add your domain to
csrf_trusted_originsincredential.pywith the full scheme:csrf_trusted_origins = ['https://your-domain.com'] - Restart Gunicorn:
sudo systemctl restart secboard - If the error persists for one specific browser, clear cookies for the domain and try again.
Cause: The request Host header is not in Django's ALLOWED_HOSTS.
Fix: Add the domain to allowed_hosts in credential.py:
allowed_hosts = ['your-domain.com', 'www.your-domain.com', '127.0.0.1']
Restart Gunicorn after the change.
Cause: The user's Access Groups do not include the required permission for the action or section.
Fix: Open Users → Groups, find the user's group, and enable the required permission. Check the Show link permission separately — it controls navigation visibility independently of content access.
Cause: collectstatic was not run, or the Nginx alias path is wrong.
Fix:
- Run:
python manage.py collectstatic --noinput - Verify the Nginx config
aliasmatchesSTATIC_ROOT(/srv/SecBoard/staticfiles/) andMEDIA_ROOT(/srv/SecBoard/media/). - Reload Nginx:
sudo systemctl reload nginx
Cause: Nginx's client_max_body_size is lower than the uploaded file size.
Fix: Set client_max_body_size 100M; in the Nginx server block and reload:
sudo systemctl reload nginx
Cause: An unhandled Python exception in the application. The browser receives a generic error page while the real traceback is in the Gunicorn error log.
Diagnostic:
tail -50 /srv/SecBoard/logs/gunicorn_error.log
Look for the last Traceback (most recent call last) block. Common causes:
- Database connection error — MySQL/MariaDB is down or credentials changed. Check
credential.pydatabase settings. - Missing migration — a new model was deployed without running
python manage.py migrate. - Import error — a Python package is missing. Run
pip install -r requirements.txtinside the virtualenv.
Cause: Gunicorn is not running, or the proxy_pass port in the Nginx config does not match the --bind port in the Gunicorn service file.
Diagnostic:
sudo systemctl status secboard
journalctl -u secboard -n 30
Fix:
- If the service is stopped:
sudo systemctl start secboard - If it keeps crashing, check
gunicorn_error.logfor the startup error. - Verify
proxy_pass http://127.0.0.1:PORT;in Nginx matches the--bind 127.0.0.1:PORTin the service file.
Cause: A request took longer than proxy_read_timeout (60 s default in the Nginx config) or the Gunicorn --timeout (120 s).
Fix options:
- Increase
proxy_read_timeoutin Nginx if the operation legitimately takes longer (e.g. large report generation). - Move long-running tasks to Celery background workers instead of synchronous request handling.
- Check if a slow database query is the root cause — look for slow query warnings in MySQL logs.
Cause: SECURE_SSL_REDIRECT = True (enabled by debug = False) but Django doesn't see the X-Forwarded-Proto: https header that Nginx should send. Django thinks the request is HTTP and redirects again.
Fix: Ensure the Nginx location / block includes:
proxy_set_header X-Forwarded-Proto $scheme;
settings.py already trusts this header via SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https').
Authentication Problems
Work through this checklist in order:
| Check | How to verify | Fix |
|---|---|---|
| No password set | User was created without a password (new accounts have no password by default) | Send user to /password_reset/ or use First Login flow at /first_login/ |
| Account inactive | Users → user profile: check Active status, Start/End dates | Tick Active, adjust dates, or clear end date |
| AD account disabled | Check AD: userAccountControl flag | Re-enable in Active Directory; SecBoard mirrors the flag on next login |
| Brute-force lockout | Multiple rapid failed attempts from one IP trigger a lockout period | Wait for the lockout to expire, or clear the lockout via the admin panel |
| Email typo | Check exact email used vs email in Users list | Correct the email in the user profile |
- Check spam / junk folder first.
- Verify a Mail Account is configured for password reset emails (Admin → App Cabinet → Cabinet Settings → Mail Account).
- Check
gunicorn_error.logforSMTPExceptionorConnectionRefusedErrorimmediately after the reset request. - Verify the sender domain has valid SPF and DKIM records — many providers silently reject mail without them.
- Confirm the user's email address in SecBoard exactly matches what they typed (no leading/trailing spaces).
- Clock skew: TOTP codes are time-based. If the server clock differs from the user's device by more than 30 seconds, codes will be rejected. Sync the server clock:
sudo timedatectl set-ntp true. - Wrong account in app: The user may have re-scanned a QR code for a different service. Ask them to delete the SecBoard entry from their authenticator and re-enrol.
- Admin reset: Open the user in Users → Edit, disable Require Two-Factor Authentication, let the user log in, and re-set up 2FA from their Personal Cabinet.
Check in order:
- Use the Test Connection button on the AD connection in the admin panel to confirm the service account bind works.
- Check
gunicorn_error.logforldap3exceptions (search forLDAPExceptionorldap3). - Verify
attr_emailis correct and the user has a non-empty value for that attribute in AD. - Verify the user is within the
base_dn/user_search_ouscope. - Check that
ldap3is installed in the virtualenv:/srv/SecBoard/venv/bin/pip show ldap3 - If LDAPS is enabled, confirm the DC certificate's common name or SAN matches the
server_url. SecBoard accepts self-signed certs, but the server URL must be reachable on port 636.
Email & Notification Errors
Diagnostic sequence:
- Trigger a password reset and immediately check
gunicorn_error.logfor an SMTP exception. - Common errors and fixes:
Error in log Cause Fix ConnectionRefusedErrorSMTP host or port is wrong, or port is blocked by firewall Verify host/port; test with telnet smtp.host.com 587SMTPAuthenticationErrorWrong username or password Re-enter the mail account password in Admin → Mail Accounts SMTPServerDisconnectedWrong TLS setting (SSL on STARTTLS port or vice versa) Use port 587 with STARTTLS or port 465 with SSL — not mixed ssl.SSLCertVerificationErrorSelf-signed or expired cert on SMTP server Enable "Allow self-signed" on the Mail Server if supported, or use a valid cert
Each feature in SecBoard can be assigned a different Mail Account. Check that the specific feature has a mail account assigned:
- Password reset / First Login: Admin → App Cabinet → Cabinet Settings → Mail Account (per company)
- Contact form auto-reply: Admin → App Conf → Contact Settings → Auto-Reply Mail Account
- Risk reports: Admin → App Risk → Risk Report Email Config → Mail Account
- Access request notifications: Admin → App Access → Email Notification Config → Mail Account
- Incident / GDPR / password recovery emails: use the default mail account from Django settings
Celery & Background Tasks
Step 1 — Check all three services:
sudo systemctl status secboard
sudo systemctl status SecBoard_celery
sudo systemctl status SecBoard_celery_beat
All three must be active (running). If any is stopped or failed:
sudo systemctl start SecBoard_celery
sudo systemctl start SecBoard_celery_beat
Step 2 — Check Redis:
sudo systemctl status redis
redis-cli ping # should return PONG
Step 3 — Check task logs:
tail -50 /srv/SecBoard/logs/celery_worker.log
tail -50 /srv/SecBoard/logs/celery_beat.log
Common causes (visible in celery_worker.log or journalctl -u SecBoard_celery -n 50):
- Database unreachable: check MySQL/MariaDB status and
credential.pydatabase settings. - Missing module: activate the virtualenv and run
pip install -r requirements.txt. - Redis not available: Celery cannot connect to its broker. Ensure Redis is running and the host/port in settings matches.
Database Errors
- Check MySQL/MariaDB is running:
sudo systemctl status mysql - Verify the database host, port, name, user, and password in
credential.py. - Test connectivity:
mysql -h HOST -u USER -p -D DBNAME - If using a remote DB, check that the SecBoard server IP is allowed to connect (MySQL
GRANTstatements or firewall rules).
Cause: New migrations were not applied after deploying an update.
Fix:
source /srv/SecBoard/venv/bin/activate
python manage.py migrate
Then restart Gunicorn: sudo systemctl restart secboard
Cause: The database or table is not using utf8mb4 charset, which is required for emoji and full Unicode support.
Fix: Ensure the database was created with:
CREATE DATABASE secboard CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Also verify credential.py includes 'OPTIONS': {'charset': 'utf8mb4'} in the database settings.
Permission & Access Errors
- Go to Users → Users → [user] → Edit. Confirm the user has at least one Access Group.
- Open Users → Groups → [group]. For each module the user should see:
- Enable the module's View permission.
- Enable the module's Show link permission — without this the module does not appear in the nav even if the user can access its content.
- Changes take effect immediately on the user's next page request (no restart needed).
The Users section is protected by the User Management permission set. Even administrators need at least View Users enabled in their Access Group — unless they are a superuser.
Fix: Grant the group the relevant User Management permissions, or set is_superuser = True for the account in the Django admin panel (/secboard_admin/auth/user/).
Cause: The operating system user running Gunicorn does not have write permission to the media/ directory.
Fix: The service file specifies ReadWritePaths=/srv/SecBoard/media. Ensure the directory is owned by the same OS user:
sudo chown -R cursor:cursor /srv/SecBoard/media
sudo chmod -R 755 /srv/SecBoard/media
Replace cursor with the actual user in your SecBoard_prod.service file.
After an Update / Redeploy
After pulling a new version of SecBoard, run this sequence to avoid most post-deploy errors:
source /srv/SecBoard/venv/bin/activate
# Install any new Python dependencies
pip install -r requirements.txt
# Apply database migrations
python manage.py migrate
# Rebuild static files
python manage.py collectstatic --noinput
# Restart the app and workers
sudo systemctl restart secboard
sudo systemctl restart SecBoard_celery
sudo systemctl restart SecBoard_celery_beat
# Reload Nginx (if config changed)
sudo nginx -t && sudo systemctl reload nginx
If the site shows a 500 error immediately after restarting, check gunicorn_error.log — a missing migration or a new required setting in credential.py are the most common causes.
Quick Diagnostics Checklist
When something breaks and you're not sure where to start:
| # | Check | Command / location |
|---|---|---|
| 1 | Is Gunicorn running? | sudo systemctl status secboard |
| 2 | Is Nginx running? | sudo systemctl status nginx |
| 3 | Is MySQL/MariaDB running? | sudo systemctl status mysql |
| 4 | Is Redis running? | sudo systemctl status redis |
| 5 | Any Python exceptions? | tail -50 /srv/SecBoard/logs/gunicorn_error.log |
| 6 | Any Nginx errors? | tail -20 /var/log/nginx/error.log |
| 7 | Are migrations applied? | python manage.py showmigrations | grep "\[ \]" |
| 8 | Is debug = False set correctly? | credential.py |
| 9 | Does the domain appear in allowed_hosts? | credential.py |
| 10 | Is disk space available? | df -h /srv/SecBoard |
gunicorn_error.log and nginx/error.log)
and the exact error message before contacting support — this dramatically speeds up diagnosis.