# NekoImageGallery Lifespan Management: FastAPI Startup and Shutdown Architecture

> Explore the NekoImageGallery lifespan management with FastAPI startup and shutdown. Discover how the ServiceProvider and LifespanService orchestrate application lifecycle hooks in this three-tier architecture.

- Repository: [EdgeNeko/nekoimagegallery](https://github.com/hv0905/nekoimagegallery)
- Tags: architecture
- Published: 2026-03-03

---

**NekoImageGallery leverages FastAPI's native lifespan context manager to orchestrate startup and shutdown through a three-tier architecture: the FastAPI app triggers a `ServiceProvider` which recursively invokes lifecycle hooks on all `LifespanService` subclasses.**

Managing application lifespan in async Python services requires careful coordination of resource initialization and cleanup. The **hv0905/nekoimagegallery** repository implements a robust **NekoImageGallery lifespan management** system using FastAPI's modern lifespan protocol. This architecture ensures that database connections, ML models, and storage backends initialize correctly on boot and release resources gracefully on termination.

## Three-Layer Lifespan Architecture

The application decouples lifecycle management into three distinct layers, with clear separation of concerns between the web framework, service orchestrator, and individual components.

### FastAPI Lifespan Context in [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py)

The entry point for lifespan management resides in [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py) at lines 23-34. Here, the FastAPI application is instantiated with an async `lifespan` context manager passed to the constructor.

During **startup**, this context manager instantiates the central `ServiceProvider` and awaits its `onload()` method. When the server receives a shutdown signal (SIGTERM or Ctrl-C), FastAPI exits the context manager's `yield` point, triggering the `onexit()` method.

### ServiceProvider Orchestration in [`app/Services/provider.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/provider.py)

Defined at lines 48-56, the `ServiceProvider` class acts as the central hub for all core services including storage, indexing, OCR, and transformers. It implements two primary methods:

- **`onload()`**: Scans the provider's own attributes for objects inheriting from `LifespanService` and executes their `on_load()` coroutines concurrently.
- **`onexit()`**: Performs the same attribute scan, invoking `on_exit()` on each discovered service in parallel to ensure rapid, non-blocking cleanup.

### Individual Service Hooks via `LifespanService`

The abstract base class `LifespanService` in [`app/Services/lifespan_service.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/lifespan_service.py) (lines 1-6) defines the interface for lifecycle-aware components. Concrete implementations such as `StorageService`, `IndexService`, and various OCR services inherit from this base.

By default, the base class methods are no-ops, but individual services override `on_load()` to initialize connections and load models, and `on_exit()` to close file handles and release GPU memory.

## Startup and Shutdown Execution Flow

When you launch the server via [`main.py`](https://github.com/hv0905/nekoimagegallery/blob/main/main.py) (lines 58-61), uvicorn mounts the FastAPI application and enters the lifespan context. The execution flow follows this hierarchy:

1. **Uvicorn** starts and enters the FastAPI lifespan context in [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py).
2. **ServiceProvider** instantiation triggers `onload()`, which discovers all `LifespanService` instances.
3. **Parallel initialization** occurs as `on_load()` runs on each service (e.g., database connections established, ML models loaded into VRAM).
4. **Server ready** state achieved; application accepts HTTP requests.
5. **Shutdown signal** received; FastAPI exits the lifespan context post-yield.
6. **Graceful cleanup** executes via `onexit()`, calling `on_exit()` on each service to release resources.

## Code Examples and Implementation Details

To start the server with automatic lifespan management:

```bash

# From the repository root, launch the application

python -m main

```

For programmatic testing where you need to trigger lifespan events manually:

```python
from fastapi.testclient import TestClient
from app.webapp import app

# TestClient automatically enters the lifespan context on initialization

# and exits on cleanup, triggering both startup and shutdown hooks

client = TestClient(app)
response = client.get("/api/")

# Shutdown hooks run automatically when the client context closes

```

Key implementation files:

- [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py): Binds the lifespan context to the FastAPI instance and coordinates the ServiceProvider lifecycle.
- [`app/Services/provider.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/provider.py): Implements the orchestration logic that distributes lifecycle events to individual services.
- [`app/Services/lifespan_service.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/lifespan_service.py): Defines the base protocol that enables dependency injection of lifecycle hooks.
- [`main.py`](https://github.com/hv0905/nekoimagegallery/blob/main/main.py): CLI entry point that launches uvicorn, ultimately triggering the entire lifespan sequence.

## Summary

- NekoImageGallery uses FastAPI's modern **lifespan context manager** protocol rather than deprecated startup/shutdown events.
- The **`ServiceProvider`** class in [`app/Services/provider.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/provider.py) centralizes service creation and delegates lifecycle events to components in parallel.
- Services opt-in to lifecycle management by inheriting from **`LifespanService`** and implementing `on_load()` and `on_exit()` hooks.
- This architecture ensures **graceful resource management**, preventing memory leaks and connection pool exhaustion during deployments and rolling restarts.

## Frequently Asked Questions

### How does NekoImageGallery hook into FastAPI lifespan events?

The application defines an async context manager in [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py) that is passed to the `FastAPI` constructor via the `lifespan` parameter. This context manager yields after calling `ServiceProvider.onload()`, allowing the server to run, and resumes on shutdown to execute `ServiceProvider.onexit()`, ensuring proper cleanup sequence.

### What happens if a service fails during startup?

Since `ServiceProvider.onload()` invokes `on_load()` on all discovered `LifespanService` instances, an exception in any single service will propagate up through the FastAPI lifespan context, preventing the server from reaching the ready state. This fail-fast behavior ensures the application never starts in a partially initialized state.

### Can services define custom shutdown priorities?

The current implementation in [`app/Services/provider.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/Services/provider.py) invokes all `on_exit()` methods concurrently using `asyncio.gather`. Services do not have explicit priority ordering; instead, they must be designed to handle parallel shutdown. If ordering is required, you would modify the provider's `onexit()` method to sequence the calls rather than running them simultaneously.

### Where is the server entry point that triggers these lifecycle stages?

The entry point is [`main.py`](https://github.com/hv0905/nekoimagegallery/blob/main/main.py) at lines 58-61, which programmatically launches uvicorn with the FastAPI application instance. When uvicorn starts, it immediately enters the lifespan context defined in [`app/webapp.py`](https://github.com/hv0905/nekoimagegallery/blob/main/app/webapp.py), triggering the entire NekoImageGallery startup sequence automatically.