Read logs before changing code
When a small blog fails in production, the cause is often simple: a missing environment variable, upload size limit, database permission issue, or service restart failure. Guessing from the browser usually wastes time.
Start by separating the request chain: browser, Nginx, Gunicorn, Flask, and database. Each layer answers a different question.
Classify the failure
Connection timeout, 502, and 503 usually point to the entry layer: Nginx, Gunicorn, or systemd. A 500 means the request reached the application but Python raised an exception. A business problem, such as comments not appearing, may have no traceback and needs database inspection.
A useful order
First, check /healthz. If it fails, debug the process before business pages.
Second, inspect Nginx error logs. Upstream connection refused usually means the app is not listening. Body size errors mean uploads are blocked before Flask.
Third, inspect service logs with journalctl. The last lines of the traceback usually contain the real cause.
Fourth, reproduce the same request with the same account, form data, and file size.
Slow requests need context
A slow homepage may come from list queries or sidebar aggregation. A slow article page may come from related-post queries or dynamic rendering. A slow admin page may need pagination or fewer summary queries.
Takeaway
Good troubleshooting starts by locating the layer. Once path, status code, duration, and traceback are aligned, most small blog incidents become much easier to resolve.