Skip to main content

singleserver

PyPI Version Python Versions License Last Commit

Manage singleton server processes across multiple workers using atomic socket binding.

Problem

When running web applications with multiple workers (e.g., gunicorn), you often need auxiliary services (API servers, background processors, caches) that should only run as a single instance shared by all workers.

Current solutions like systemd unit files or separate containers require additional infrastructure and make local development different from production.

Solution

singleserver uses the OS-level guarantee that only one process can bind a socket at a time. When multiple workers call connect():

  1. First worker acquires the lock and starts the server
  2. Other workers detect the lock is held and connect as clients
  3. If the owner dies, another worker can take over
Worker 1: connect() → bind(:18765) → SUCCESS → starts server, returns client
Worker 2: connect() → bind(:18765) → EADDRINUSE → waits for ready, returns client
Worker 3: connect() → bind(:18765) → EADDRINUSE → waits for ready, returns client

Installation

pip install singleserver

Quick Start

from singleserver import SingleServer

# Define the server
api_server = SingleServer(
    name="api-server",
    command=["python", "-m", "my_api", "--port", "{port}"],
    port=8765,
)

# Connect (starts if needed, connects if already running)
with api_server.connect() as client:
    response = client.get("/data.json")
    print(response.json())

Features

  • Atomic coordination: Uses socket binding for distributed locking
  • Health monitoring: Configurable health checks with automatic restarts
  • Output handling: Redirect stdout/stderr to files
  • Graceful shutdown: SIGTERM with timeout, then SIGKILL
  • No external dependencies: Pure Python, no Redis/etcd/etc needed
  • Same code for dev and prod: No systemd unit files required

Configuration

SingleServer(
    name="my-service",                    # Identifier for logging
    command=["python", "server.py", ...], # Command to run ({port} is replaced)

    # Server address
    port=8765,                           # TCP port
    # OR
    socket="/tmp/my-service.sock",       # Unix socket (faster, more secure)

    # Health checks
    health_check_url="/",                # URL to poll for readiness
    health_check_interval=5.0,           # Seconds between checks
    startup_timeout=30.0,                # Max seconds to wait for startup

    # Restart behavior
    restart_on_failure=True,             # Auto-restart if process dies
    max_restarts=3,                      # Give up after N restarts
    restart_delay=1.0,                   # Seconds between restart attempts

    # Process options
    env={"DATABASE_URL": "..."},         # Environment variables
    cwd="/app",                          # Working directory
    stdout="/var/log/my-service.log",    # Log file
    stderr="stdout",                     # Redirect stderr to stdout

    # Shutdown
    shutdown_timeout=10.0,               # Seconds for graceful stop
)

Use Cases

Internal API server

from singleserver import SingleServer

api = SingleServer(
    name="internal-api",
    command=["python", "-m", "internal_api", "-p", "{port}"],
    port=8765,
)

def my_view(request):
    with api.connect() as client:
        data = client.get("/query").json()
    return JsonResponse(data)

Background service

service = SingleServer(
    name="background-service",
    command=["python", "service.py", "--port", "{port}"],
    port=8888,
    health_check_url="/health",
    restart_on_failure=True,
)

How It Works

  1. Lock acquisition: Uses a separate socket (port + 10000 by default) for coordination
  2. Process management: Owner spawns subprocess in new process group
  3. Health monitoring: Background thread polls health endpoint
  4. Restart logic: Configurable restart attempts with delay
  5. Cleanup: atexit handler and signal handlers for clean shutdown

Development

# Install dev dependencies
uv sync --all-extras

# Run tests
uv run pytest

# Type checking
uv run mypy singleserver

# Linting
uv run ruff check singleserver tests

License

MIT

Metadata

Release files for singleserver 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for singleserver 0.4.0
File Size Uploaded
singleserver-0.4.0.tar.gz 84.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for singleserver 0.4.0
File Interpreter ABI Platform
singleserver-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 102.1 kB

Release files / singleserver-0.4.0.tar.gz

Download URL singleserver-0.4.0.tar.gz
Size 84.7 kB
Tags Source
SHA-256 checksum
How to use checksums
cab015d4c85acd77187878e4e0843239c1305ca567e446b6e5fe8f83dcadfbea
BLAKE2b-256 checksum
How to use checksums
d90760acbe286c6bea14cee4e5342c8a9fea25a91bba2d818b2fece349fc49a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.2

Release files / singleserver-0.4.0-py3-none-any.whl

Download URL singleserver-0.4.0-py3-none-any.whl
Size 17.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
867762d5b26c393137a9dbcf033e85032c6a91375793bca088511147ba2be895
BLAKE2b-256 checksum
How to use checksums
8ca03a1af4e740cdbcb6b3bd0da0e21d72642b68723974fe744c5ea7dbbcbe87
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.2

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page