PocketBase 502 on Railway — graceful connect and restart pattern
A 502 from your VeloCMS Railway deployment almost always means PocketBase restarted and Next.js hasn't reconnected yet. Here's the graceful connect pattern and how to configure Railway health checks to minimise downtime.
A 502 on your VeloCMS Railway deployment almost always means PocketBase has restarted and the Next.js service is trying to connect to a PocketBase instance that isn't yet ready. Railway restarts PocketBase whenever it deploys a new version, runs out of memory, or is manually restarted from the Dashboard. With a graceful connect pattern, the Next.js service retries the connection transparently instead of crashing.
Why 502 happens on Railway
Railway runs both your Next.js service and PocketBase as separate containers. When PocketBase restarts, there's a window of 5–30 seconds where the container is starting but not yet accepting connections. If Next.js tries to make a PocketBase request during that window — on the first SSR render, for example — PocketBase returns a connection refused error, which Next.js translates to a 502 to the client.
The graceful connect pattern
VeloCMS's PocketBase client wrapper wraps the connect() call in a try-catch with retry logic. If the first connection attempt fails, the client retries with exponential backoff — 1s, 2s, 4s, up to 30s — before throwing a permanent error. During the retry window, SSR pages render a skeleton or loading state instead of a 502.
// Graceful connect pattern in src/lib/pocketbase/server.ts:
async function connect(): Promise<PocketBase> {
let attempt = 0;
while (attempt < 5) {
try {
const db = new PocketBase(process.env.POCKETBASE_URL);
await db.health.check();
return db;
} catch (err) {
attempt++;
if (attempt === 5) throw err;
await new Promise((r) => setTimeout(r, 1_000 * 2 ** attempt));
}
}
throw new Error("PocketBase unavailable after 5 attempts");
}Railway health check configuration
In Railway your PocketBase service Settings Health Check, set the health check path to /api/health (PocketBase's built-in health endpoint) and the start period to 30s. The start period tells Railway not to route traffic to the container until 30 seconds after startup — giving PocketBase time to initialise SQLite and start accepting connections before any requests arrive.
If you're self-hosting and using a single Railway service for both Next.js and PocketBase, the health check path needs to route to PocketBase specifically — not the Next.js health route. Use Railway's RAILWAY_HEALTHCHECK_TIMEOUT_SEC variable to extend the health check timeout for larger PocketBase databases that take longer to initialise.
Monitoring for recurring 502s
If you're seeing 502s regularly (more than once per week), the root cause is likely memory pressure rather than deployment restarts. Railway's free tier containers have 512MB RAM — PocketBase with a large SQLite database and heavy write load can exceed this. Upgrade to a container with at least 1GB RAM, or enable PocketBase's --queryTimeout flag to prevent long-running queries from consuming all available memory. Monitor memory usage in Railway Observability Metrics.