VayuPress Docs

VayuPress Troubleshooting

Health Check Failures

/health returns 500

VayuPress process is not running or crashed.

sudo systemctl status vayupress
sudo journalctl -u vayupress -n 50

/health/ready returns 503

A required subsystem (DB, storage) is unavailable.

curl http://localhost:8080/health/dependencies
# Check which component reports "unavailable"

/health/meilisearch returns 503

Meilisearch is down — this is non-critical. VayuPress falls back to SQLite LIKE queries.

sudo systemctl status meilisearch
sudo systemctl restart meilisearch

Write Queue Issues

Articles not appearing after POST

Writes are asynchronous. Check the queue:

sqlite3 /var/lib/vayupress/vayupress.db "SELECT status, COUNT(*) FROM write_jobs GROUP BY status"

If jobs are stuck in processing:

# The stuck-job reaper runs every minute and resets jobs stuck >5 min
# Or manually reset:
sqlite3 /var/lib/vayupress/vayupress.db "UPDATE write_jobs SET status='pending' WHERE status='processing'"
sudo systemctl restart vayupress

Jobs in dead-letter queue

sqlite3 /var/lib/vayupress/vayupress.db "SELECT id, op, dead_reason FROM write_jobs WHERE status='dead'"

Jobs move to dead after MAX_REPLAY_COUNT (default 3) replay attempts. Check logs for the root cause.

Database Issues

SQLite locked errors

WAL mode is mandatory. Verify:

sqlite3 /var/lib/vayupress/vayupress.db "PRAGMA journal_mode"
# Expected: wal

If not WAL:

sqlite3 /var/lib/vayupress/vayupress.db "PRAGMA journal_mode=WAL"

Migration checksum drift (startup halted)

VayuPress detected a tampered migration. Check logs:

sudo journalctl -u vayupress | grep "CHECKSUM DRIFT"

This is a security alert. Do not bypass it without understanding why the checksum changed.

Database corruption

sqlite3 /var/lib/vayupress/vayupress.db "PRAGMA integrity_check"
# Expected: ok

If corrupt, restore from backup:

ls -lt /backups/vayupress-*.db.gz | head -5
# Restore:
sudo systemctl stop vayupress
gunzip -c /backups/vayupress-<date>.db.gz > /var/lib/vayupress/vayupress.db
sudo systemctl start vayupress

Cache Issues

Stale content after update

Force-purge the cache for an article:

curl -X POST http://localhost:8080/api/v1/cache/purge \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-CSRF-Token: $CSRF_TOKEN" \
  -d '{"slug": "my-article"}'

Or clear all cache:

sudo rm -rf /var/cache/vayupress/posts/*

Cache directory unwritable

sudo chown -R www-data:www-data /var/cache/vayupress
sudo chmod -R 755 /var/cache/vayupress

Performance Issues

High memory usage

curl http://localhost:8080/metrics | grep vayu_memory

If idle RAM >800 MB, check for goroutine leaks:

curl http://localhost:6060/debug/pprof/goroutine?debug=1

High TTFB

curl http://localhost:8080/metrics | grep vayu_http_request_duration

Check if Nginx is serving static files (should be, for cached pages):

nginx -t && sudo systemctl status nginx

Storage Issues

Storage quota exceeded (413 on upload)

df -h /var/www/vayupress
curl http://localhost:8080/health/storage

Increase STORAGE_QUOTA_GB in the environment, or delete old media.

Orphaned media files

The orphan cleanup cron runs daily. To run manually:

curl -X POST http://localhost:8080/admin/storage/cleanup \
  -H "Authorization: Bearer $API_KEY"

VayuTalk Chat Issues

Messages queue / never deliver live; web shows "Reconnecting…"; the mobile app receives nothing

VayuTalk delivers messages and read receipts over a long-lived Server-Sent Events stream (GET /api/v1/talk/stream for the app, GET /os/talk/stream for the web console). If that stream can't stay open, messages only ever drain from the queue during brief connect windows — so live delivery and read receipts silently fail, the web status dot sticks on "Reconnecting…", and the app (which can send via POST but can't hold the stream) appears to receive nothing. Two layers in front of VayuPress commonly break the stream:

1. Reverse proxy (nginx) buffering / idle timeout. A generic proxy block buffers the response and times the connection out (nginx defaults: proxy_read_timeout 60s). The SSE endpoints need dedicated location blocks:

    location = /api/v1/talk/stream {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off; proxy_cache off; gzip off;
        chunked_transfer_encoding on;
        proxy_read_timeout 3600s; proxy_send_timeout 3600s;
    }
    location = /os/talk/stream {
        # …same, plus:  proxy_pass_header X-CSRF-Token;
    }

deploy-vayupress.sh writes these automatically; an existing server needs them added and nginx -s reload. Verify with curl -N https://DOMAIN/api/v1/talk/stream — you should get a JSON invalid-token error (or an event: ping), NOT an immediate disconnect.

2. A CDN / bot challenge in front of nginx (e.g. Cloudflare). A browser can solve a JavaScript "managed challenge," so the web may work — but the mobile app is a plain HTTP client that cannot solve it, so Cloudflare returns a Just a moment… HTML page instead of the event stream and the app's stream never connects. Diagnose it:

curl -N -s https://DOMAIN/api/v1/talk/stream -H 'User-Agent: Go-http-client/2.0' | head -c 300

If you see <!DOCTYPE html>… Just a moment… (look for cf-ray, challenges.cloudflare.com), the CDN is intercepting the stream. Do not turn bot protection off site-wide — exempt only the VayuTalk paths. In Cloudflare: Security → WAF → Custom rules → Create rule, expression:

(starts_with(http.request.uri.path, "/api/v1/")) or (starts_with(http.request.uri.path, "/os/talk/"))

Action Skip → skip managed rules, Bot Fight Mode, and (if shown) Security Level / Browser Integrity Check. The rest of the site keeps full CDN protection; only the VayuTalk API/stream paths pass straight through. When fixed, the curl above returns VayuPress's JSON, and the app connects. The same principle applies to any CDN or WAF — allow the two stream paths, keep protecting everything else.

If your site sits behind a CDN you can't fully exempt (e.g. a WAF Skip rule isn't enough against a heavy bot swarm), the robust fix is to serve the VayuTalk relay on its own subdomain that bypasses the CDN entirely, while the main domain keeps full protection. VayuPress automates the entire server side — the only manual step is one DNS record:

talk.<domain>   A / AAAA   ->  your server's IP    (CDN/proxy OFF — "DNS only")

Then run the deploy script (every VayuPress update runs it):

sudo bash scripts/deploy-vayupress.sh

It automatically: adds talk.<domain> to the Let's Encrypt certificate, writes a dedicated nginx vhost that exposes only the relay API (SSE never buffered), and sets VAYUOS_TALK_HOST, which makes the server advertise the host in /.well-known/vayumail/autoconfig.json. The VayuMail app reads that, confirms the subdomain is live, and routes its chat stream there on its own — no app setting to change. Until the DNS record exists the step is skipped and the app keeps using the main domain, so nothing breaks in the meantime.

Verify after DNS propagates:

curl -s https://<domain>/health | grep -o '"version"[^,]*'          # confirm the running build
curl -N -s https://talk.<domain>/api/v1/talk/stream                 # expect JSON invalid-token, NOT a CDN page

Because your origin IP is already reachable via mail.<domain> (mail must also be CDN-proxy-off), exposing talk.<domain> adds no new exposure. To keep the CDN's protection meaningful for the main site even though the IP is public, restrict the main <domain> vhost to your CDN's published IP ranges (allow those, deny the rest); talk.<domain> stays open. The relay itself is safe exposed: it is auth-gated, server-side rate-limited, and size-capped.

VayuMail Sign-In Issues

Someone forgot their mailbox password

Send them to https://<your-domain>/mail/recover. It is linked from every sign-in page as "Forgot your mailbox password?".

Which path works depends on what they enrolled before they were locked out:

They have Send them to
A recovery address /mail/recover
Recovery codes /mail/recover/code
A phone still syncing the mailbox the mobile app's own reset
Nothing /mail/recover/ask — then approve it in VayuOS

Two things that look like faults and are not:

  • "Check your inbox" for an address that does not exist. The response is identical for a real mailbox and a typo, by design — the endpoint must not reveal which addresses exist. If nothing arrives, check the spelling first.
  • Every phone needs re-enrolling after a reset. A reset revokes all app passwords deliberately, so a stale credential cannot survive the recovery.

To see the state of an install from the host:

vayupress mail unrecoverable                     # mailboxes with no factor at all
vayupress mail recovery someone@yourdomain.com   # what one mailbox has enrolled

Full guide, including the last-administrator break-glass: docs/MAIL-RECOVERY.md.

The recovery address option is missing or disabled

Expected in a Tor Space. It needs clearnet egress, which a Tor Space refuses by design, so it is disabled and labelled rather than failing at the moment it is needed. Recovery codes and the mobile-app path both work there.

PGP Key Discovery Issues

gpg --locate-keys you@example.com finds nothing

VayuPress mints a PGP keypair for every mailbox and serves the Web Key Directory, so if discovery finds nothing the keys almost certainly exist and are simply not reachable where clients look. There are exactly three causes, in the order worth checking.

1. openpgpkey.<domain> is not pointed at the server. PGP clients do not look on your main domain — the WKD specification defines a dedicated hostname, and without it your keys are published where nobody checks. Confirm:

getent hosts openpgpkey.example.com     # no output = not pointed

Add an A/AAAA record for openpgpkey.example.com → your server, CDN proxy OFF, then run:

sudo bash /path/to/VayuPress/scripts/setup-openpgpkey-subdomain.sh

It provisions the certificate and vhost, and self-checks afterwards. With CF_ZONE_ID and CF_API_TOKEN set in /etc/vayupress/env it creates the record for you.

2. The CDN proxy is on for that record. This is the failure worth understanding, because nothing appears broken: a key fetch is machine-to-machine, GnuPG has no JavaScript engine and cannot answer a bot challenge, so discovery fails and correspondents quietly fall back to sending in cleartext. No error surfaces anywhere. Fetch the policy file — HTML instead of an empty 200 means the proxy is intercepting:

curl -sSI "https://openpgpkey.example.com/.well-known/openpgpkey/example.com/policy"

Set the record to DNS only (Cloudflare: grey cloud). Do not disable protection site-wide; only this host needs to be direct, and its vhost serves one path prefix and returns 404 for everything else.

3. VayuPGP is switched off. With the engine disabled the directory returns 404 even though the host and certificate are perfect — which looks identical to a DNS problem from outside. Check VayuOS → VayuPGP. The setup script distinguishes these two for you: it fetches its own policy file after reloading nginx and says which of them it is.

Keys resolve for the primary domain but not a secondary one

Discovery is per-domain: a key for someone@shop.example is findable only at openpgpkey.shop.example, never via the primary domain's host. Every mail-enabled domain needs its own record. The setup script provisions the primary plus every mail-enabled VayuDomains secondary (vayupress domains hosts --mail) in one run, so this usually means the secondary's record is missing:

sudo bash /path/to/VayuPress/scripts/setup-openpgpkey-subdomain.sh shop.example

Logs

# VayuPress logs (structured JSON)
sudo journalctl -u vayupress -f

# Nginx access logs
sudo tail -f /var/log/nginx/access.log

# Meilisearch logs
sudo tail -f /var/log/meilisearch/meilisearch.log

Support

support@vayupress.comhttps://vayupress.com/docs