- Add frontend/public/robots.txt: allow public pages, block auth/user-private routes and /api/ - ROADMAP.md: add mobile layout fix to UX & Polish shipped items - TROUBLESHOOTING.md: add section on Android Chrome viewport overflow root cause and fix Authored by: Jack Levy
5.4 KiB
Troubleshooting
Common issues encountered during development and deployment of PocketVeto.
502 Bad Gateway after rebuilding a container
Symptom
All API calls return 502. nginx error log shows:
connect() failed (111: Connection refused) while connecting to upstream,
upstream: "http://172.18.0.X:8000/api/..."
The IP in the error is the old IP of the container before the rebuild.
Root cause
When nginx uses upstream blocks, it resolves hostnames once at process startup and caches the result for the lifetime of the process. Rebuilding a container (e.g. docker compose build api && docker compose up -d api) assigns it a new Docker network IP. nginx still holds the old IP and all connections are refused.
Immediate fix
docker compose restart nginx
This forces nginx to re-resolve all upstream hostnames from Docker's internal DNS (127.0.0.11).
Permanent fix (already applied)
Replace upstream blocks with set $variable in proxy_pass. nginx only activates the resolver directive when a variable is used — making it re-resolve on each request cycle (every valid=N seconds).
resolver 127.0.0.11 valid=10s ipv6=off;
# BAD — resolves once at startup, caches forever
upstream api {
server api:8000;
}
location /api/ {
proxy_pass http://api;
}
# GOOD — re-resolves via resolver every 10 s
location /api/ {
set $api http://api:8000;
proxy_pass $api;
}
Wrong service name for docker compose exec
The API service is named api in docker-compose.yml, not backend.
# Wrong
docker compose exec backend alembic upgrade head
# Correct
docker compose exec api alembic upgrade head
Alembic migration not applied after rebuild
If a new migration file was added after the last image build, the API container won't have it baked in. The container runs alembic upgrade head at startup from the built image.
Fix: rebuild the API image so the new migration file is included, then restart:
docker compose build api && docker compose up -d api
Wrong postgres user
The database superuser is congress (set via POSTGRES_USER in .env / docker-compose.yml), not the default postgres.
# Wrong
docker compose exec postgres psql -U postgres pocketveto
# Correct
docker compose exec postgres psql -U congress pocketveto
Frontend changes not showing after editing source files
The frontend runs as a production Next.js build (NODE_ENV=production) — there is no hot reload. Code changes require a full image rebuild:
docker compose build frontend && docker compose up -d frontend
Static assets are cache-busted automatically by Next.js (content-hashed filenames), so a hard refresh in the browser is not required after the new container starts.
Celery tasks not reflecting code changes
Celery worker and beat processes also run from the built image. After changing any worker code:
docker compose build worker beat && docker compose up -d worker beat
Checking logs
# All services
docker compose logs -f
# Single service (last 50 lines)
docker compose logs --tail=50 api
docker compose logs --tail=50 nginx
docker compose logs --tail=50 worker
# Follow in real time
docker compose logs -f api worker
Mobile page wider than viewport (Android Chrome zoom / dead space)
Symptom
On Android Chrome, a page zooms out to fit an oversized layout. The content is readable but the bottom third of the screen is dead space (background color only). The same page looks fine on all other devices, and other pages on the same device are fine.
Root cause
Flex items default to min-width: auto, meaning they cannot shrink below their content's natural width. If any descendant (e.g. a long URL in an input field) is wider than the viewport, the containing flex column expands to match, making the entire page layout wider than the viewport. Android Chrome then zooms the viewport out to fit the full layout width — which makes the layout taller than the visual viewport, leaving dead space at the bottom.
Fix
Add min-w-0 (Tailwind) / min-width: 0 (CSS) to the flex item that forms the main content column. This allows the column to shrink to zero, enabling children to wrap and truncate normally.
In AuthGuard.tsx the content column needs both flex-1 and min-w-0:
// Before — missing min-w-0
<div className="flex-1 flex flex-col min-h-0">
// After
<div className="flex-1 flex flex-col min-h-0 min-w-0">
Also switch the outer shell from h-dvh to fixed inset-0 flex overflow-hidden so the shell is anchored to the visual viewport directly (avoids dvh staleness issues on Android):
// Before
<div className="flex h-dvh bg-background">
// After
<div className="fixed inset-0 flex overflow-hidden bg-background">
General rule: any flex-1 column that can contain user-supplied text or long URLs should also carry min-w-0.
Inspecting the database
docker compose exec postgres psql -U congress pocketveto
Useful queries:
-- Recent notification events
SELECT event_type, bill_id, dispatched_at, created_at
FROM notification_events
ORDER BY created_at DESC
LIMIT 20;
-- Follow modes per user
SELECT u.email, f.follow_type, f.follow_value, f.follow_mode
FROM follows f
JOIN users u ON u.id = f.user_id
ORDER BY u.email, f.follow_type;
-- Users and their RSS tokens
SELECT id, email, rss_token IS NOT NULL AS has_rss_token FROM users;