# Ghost Boot Sequence Explained: From Cold Start to Running Server in TryGhost/Ghost

> Explore the Ghost boot sequence in TryGhost Ghost. Understand the 10 initialization phases, from configuration to running server, governed by bootGhost() for a smooth platform startup.

- Repository: [Ghost/Ghost](https://github.com/TryGhost/Ghost)
- Tags: internals
- Published: 2026-05-18

---

**The Ghost boot sequence is a 10-phase asynchronous orchestration governed by the `bootGhost()` function in [`ghost/core/core/boot.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/boot.js), which systematically initializes configuration, database connections, core services, Express applications, and background jobs to transition the platform from a cold process to a fully operational publishing server.**

The initialization logic in the TryGhost/Ghost repository is deliberately linear and heavily instrumented to ensure dependencies load in strict order. Whether starting a production instance or spinning up a test environment, the `bootGhost()` entry point coordinates every aspect of the platform lifecycle through discrete initialization phases.

## Entry Point and Boot Architecture

The `bootGhost()` function exported from [`ghost/core/core/boot.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/boot.js) serves as the sole gateway for starting the application. It accepts an options object—typically `{backend: true, frontend: true, server: true}`—that determines which components to initialize. This design allows developers to boot only the backend API for testing or enable the full stack including the HTTP server.

The sequence is structured as a series of `await` statements rather than parallel initialization, ensuring that foundational services like configuration and logging exist before dependent systems attempt to load.

## Phase 1: Foundation Loading (Configuration and Logging)

The boot process begins by loading the bare-minimum foundations required for error handling and diagnostics. According to the source code in [`ghost/core/core/boot.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/boot.js), this phase executes between lines **L76-L99**.

First, the system loads version information via `require('@tryghost/version')`, which logging tools and Sentry require for proper tagging. Immediately after, the configuration singleton loads from `./shared/config` (**L81-L83**), followed by the logging infrastructure (`@tryghost/logging` and `@tryghost/metrics` at **L86-L89**).

Before proceeding further, the process installs a global unhandled rejection handler:

```javascript
process.on('unhandledRejection', (reason, promise) => {
    logging.error('Unhandled rejection:', reason);
});

```

This ensures that any stray Promise failures during subsequent async operations are captured and logged. If configuration or logging initialization fails, the entire boot sequence halts immediately within a `try...catch` block, as the system cannot recover without these primitives.

## Phase 2: Early Service Initialization

Once diagnostics are operational, the sequence initializes error tracking and server infrastructure. This phase spans lines **L9-L44** and **L70-L84** in [`boot.js`](https://github.com/TryGhost/Ghost/blob/main/boot.js).

**Sentry** initializes first via `require('./shared/sentry')` (**L9-L11**) to capture any errors during the remaining startup sequence. Optionally, if metrics collection is enabled, `initPrometheusClient({config})` executes at **L13-L16**.

The system then creates a minimal root Express application by invoking `require('./app')()` (**L18-L20**), which starts in maintenance mode. If the `server` option is truthy, the code instantiates `GhostServer` from [`./server/ghost-server.js`](https://github.com/TryGhost/Ghost/blob/main/./server/ghost-server.js) (**L22-L25**), preparing the HTTP listener.

Database readiness follows through `initDatabase({config})`, which delegates to `DatabaseStateManager.makeReady()` (**L70-L84**). This manager executes pending migrations and verifies schema compatibility. Once connected, Sentry hooks into the Knex connection for query tracing at `sentry.initQueryTracing(connection)` (**L40-L44**).

## Phase 3: Core System Initialization (initCore)

With the database and server infrastructure ready, `initCore()` establishes services that must exist before any request handling begins (lines **L96-L158**).

The phase loads **URL utilities** (`./shared/url-utils`) early for timing purposes, followed by the **limits service** via `await limits.init()` (**L101-L105**), which creates resource caps used by the settings module. The **settings service** then hydrates all `settings` rows from the database and synchronizes the email verification flag (**L107-L112**).

Internationalization loads next via `await i18n.init()` (**L114-L117**), parsing all locale files into memory. The **URL service** initializes at **L119-L129** with either lazy or eager loading depending on configuration, registering cleanup tasks when eager. Finally, background job queues initialize through `jobService.initTestMode()` and `mentionsJobService.initTestMode()` (**L132-L158**), establishing the async work infrastructure.

## Phase 4: Frontend and Theme Services

When the `frontend` option is enabled, the sequence executes `initServicesForFrontend()` and `initFrontend()` to prepare the presentation layer.

**Route settings** load the [`routes.yaml`](https://github.com/TryGhost/Ghost/blob/main/routes.yaml) configuration (**L78-L81**), while **custom redirects** parse [`redirects.json`](https://github.com/TryGhost/Ghost/blob/main/redirects.json) (**L84-L86**) and **link redirects** initialize click-tracking URL handlers (**L88-L90**). The **theme service** loads the active theme and triggers custom theme settings initialization at **L98-L101**, followed by marketing **offers** at **L104-L107**.

Frontend template helpers—such as `{{asset}}` and `{{date}}`—initialize separately via:

```javascript
const helperService = require('./frontend/services/helpers');
await helperService.init();

```

This separation ensures the theme engine is fully prepared before the system mounts HTTP routes.

## Phase 5: Express Application Assembly

The system now constructs the HTTP request pipeline through `initExpressApps()`. A parent Express instance loads from `./server/web/parent/app`, then mounts backend and frontend sub-applications using virtual hosting:

```javascript
const parentApp = require('./server/web/parent/app')();
const backendApp = require('./server/web/parent/backend')();
parentApp.use(vhost(config.getBackendMountPath(), backendApp));

const frontendApp = require('./server/web/parent/frontend')({urlService});
parentApp.use(vhost(config.getFrontendMountPath(), frontendApp));

```

Following mount configuration, `initDynamicRouting()` parses the routes file and builds the router manager, while `initAppService()` loads administrative applications such as posts and stats (lines **78-96**).

## Phase 6: Final Service Initialization and Launch

The remaining core services start in parallel via `await Promise.all([...])` within `initServices()` (lines **13-40**). This batch includes **identity tokens**, **Stripe**, **members**, **tiers**, **permissions**, **IndexNow**, **webhooks**, **post-scheduling**, **comments**, **media-inliner**, **donations**, **gifts**, **recommendations**, **stats**, and **automations**.

Once all services signal readiness, the system mounts the fully initialized Ghost application onto the root Express instance:

```javascript
rootApp.disable('maintenance');
rootApp.use(config.getSubdir(), ghostApp);

```

This transition disables maintenance mode and exposes the complete application stack. The boot sequence concludes by notifying the parent process that the server is ready via `notifyServerReady()`, then launches background jobs through `initBackgroundServices()` (lines **78-86**), including theme caching, ActivityPub, email analytics, and update checks.

## Programmatic Usage Examples

For testing scenarios or custom integrations, you can invoke the boot sequence programmatically without starting the HTTP server:

```javascript
const bootGhost = require('ghost/core/core/boot');

// Full production boot with server
bootGhost({backend: true, frontend: true, server: true})
    .then((ghostServer) => {
        console.log('Ghost is listening on', ghostServer.address);
    })
    .catch((err) => {
        console.error('Boot failed:', err);
        process.exit(1);
    });

```

To obtain only the Express application for unit testing:

```javascript
bootGhost({backend: true, frontend: true, server: false})
    .then((rootApp) => {
        // rootApp is the configured Express instance
        // suitable for supertest or similar frameworks
    });

```

## Summary

The Ghost boot sequence follows a strict dependency chain through ten distinct phases:

- **Foundation loading**: Version info, configuration, logging, and global error handlers initialize first (L76-L99).
- **Early services**: Sentry, Prometheus, minimal Express app, GhostServer, and database connections establish the runtime environment (L9-L44, L70-L84).
- **Core initialization**: URL utils, limits, settings, i18n, and job queues prepare the business logic layer (L96-L158).
- **Frontend preparation**: Routes, redirects, themes, and template helpers load when frontend functionality is required.
- **Express mounting**: Backend and frontend applications mount on a parent Express instance with virtual host routing.
- **Service completion**: Remaining services (payments, members, webhooks) start in parallel.
- **Launch**: Maintenance mode disables, the full application mounts, and background jobs begin processing.

## Frequently Asked Questions

### What is the entry point for the Ghost boot sequence?

The entry point is the `bootGhost()` function exported from [`ghost/core/core/boot.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/boot.js). This async function accepts an options object specifying which components to initialize—such as `backend`, `frontend`, and `server`—and returns a promise that resolves to either a `GhostServer` instance or the root Express application depending on the configuration.

### How does Ghost handle database initialization during startup?

Database initialization occurs through the `initDatabase()` helper, which instantiates `DatabaseStateManager` from [`ghost/core/core/server/data/db/database-state-manager.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/data/db/database-state-manager.js). The manager runs pending migrations via `makeReady()` and loads schema information through `databaseInfo.init()`. This step occurs early in the sequence (L70-L84) because most subsequent services require database connectivity.

### What is the difference between initCore and initServices in Ghost?

`initCore()` initializes foundational services that must exist before other components load, including URL utilities, limits, settings, internationalization, and the URL service (L96-L158). `initServices()` executes later and initializes the remaining business logic—such as Stripe, members, permissions, and webhooks—via `Promise.all()` once core dependencies are available (L13-L40).

### Can I start Ghost without the HTTP server for testing?

Yes. Pass `server: false` in the options object to `bootGhost()`. This configuration skips `GhostServer` instantiation and returns the root Express application instead, allowing you to mount the application in test frameworks like Mocha or Jest without binding to a network port.