TryGhost/Ghost · error · Error

Timeout waiting for Ghost to become ready

Error message

Timeout waiting for Ghost to become ready

What it means

The readiness loop's overall timeout elapsed: the container is still running and not unhealthy, but probeHostReadiness() (fetch to /ghost/api/admin/authentication/setup through the gateway) never returned ok within the budget (default 120000ms, polled every READINESS_POLL_INTERVAL_MS). The manager dumps logs and throws. The container is slow to boot, not broken.

Source

Thrown at e2e/helpers/environment/service-managers/ghost-manager.ts:522

                throw new Error('Ghost container became unhealthy during initialization');
            }

            if (!info.State.Running) {
                const logs = await container.logs({stdout: true, stderr: true, tail: 100});
                logging.error(`Container stopped unexpectedly:\n${logs.toString()}`);
                throw new Error('Ghost container stopped during initialization');
            }

            // Still starting - wait and check again
            await new Promise((r) => {
                setTimeout(r, READINESS_POLL_INTERVAL_MS);
            });
        }

        // Timeout
        const logs = await container.logs({stdout: true, stderr: true, tail: 100});
        logging.error(`Timeout waiting for container. Last logs:\n${logs.toString()}`);
        throw new Error('Timeout waiting for Ghost to become ready');
    }

    private async probeHostReadiness(): Promise<boolean> {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 500);

        try {
            const response = await fetch(`http://localhost:${this.getGatewayPort()}/ghost/api/admin/authentication/setup`, {
                method: 'GET',
                headers: {Accept: 'application/json'},
                signal: controller.signal
            });
            const body = await response.json().catch(() => null) as {setup?: Array<{status?: unknown}>} | null;

            return response.ok && Array.isArray(body?.setup) && typeof body.setup[0]?.status === 'boolean';
        } catch (error) {
            debug('Host readiness probe failed:', error);
            return false;

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Raise the timeout: call waitForReady(<larger ms>) or increase the default if the suite consistently needs more.
  2. Check the dumped logs — if migrations are running, pre-warm them or use a migrated seed image.
  3. Verify the gateway routes localhost:<getGatewayPort()> to the Ghost container; test the URL manually.
  4. Reduce concurrent worker count on the CI runner to lower boot contention.

Example fix

// before
await ghostManager.waitForReady(); // default 120000ms

// after
await ghostManager.waitForReady(240000); // allow slow boots / migrations
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the gateway path is reachable before the test runs:
const ok = await fetch(`http://localhost:${gatewayPort}/ghost/api/admin/authentication/setup`).then(r => r.ok).catch(() => false);

Try / catch

try {
    await ghostManager.waitForReady(120000);
} catch (e) {
    // Retry once with a larger budget for slow boots / first-run migrations.
    await ghostManager.waitForReady(240000);
}

Prevention

When it happens

Trigger: Ghost is genuinely slow to start (cold cache, slow migrations, slow DB). The gateway path is misrouted so the probe never reaches Ghost. Network/proxy overhead on the gateway port. Heavy CI machine under load.

Common situations: First-run DB migrations on a fresh schema; overloaded CI runner; gateway port forwarding broken; Ghost boot loop that hasn't yet tripped the healthcheck; tight default timeout on a slow arch.

Understand the failure class

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/e6ddd7bc7fbdcd5f. Report an issue: GitHub.