Shared Nothing Architecture

Production engineering guide for shared nothing architecture covering patterns, implementation strategies, and operational best practices.

Shared Nothing Architecture is a cloud deployment pattern where each service instance operates independently, storing no state in shared storage or memory. Each node manages its own data, configuration, and runtime context, enabling maximum scalability, resilience, and autonomy. It matters when systems must scale rapidly under unpredictable load, tolerate node failures without cascading failure, and allow independent deployment and evolution of services.

When to Choose Shared Nothing Architecture

Use Shared Nothing Architecture when services must scale independently, tolerate node failures, and evolve without coordination. It is ideal for systems where latency is critical and where the cost of shared state (like a centralized database or distributed cache) outweighs the benefits of consistency and co-location.

Consider it for:

Deployment and Configuration

Each service instance is self-contained and stateless. On startup, it reads its configuration from a file, environment variables, or a centralized configuration service, then binds to a network address and begins accepting requests.

Configuration is typically applied via environment variables and a configuration file. The following example shows a service configured with env and config.json:

{
  "listen": "0.0.0.0:8080",
  "database": {
    "host": "db.example.com",
    "port": 5432,
    "name": "orders",
    "user": "app_user"
  },
  "cache": {
    "type": "redis",
    "urls": ["redis://cache-1:6379", "redis://cache-2:6379"]
  },
  "metrics": {
    "endpoint": "/metrics",
    "reporting_interval_seconds": 30
  }
}

Startup command:

env \
  DATABASE_URL="postgres://app_user:[email protected]:5432/orders" \
  LOG_LEVEL="info" \
  NODE_ID="worker-123" \
  /app/order-service --config config.json

The service reads the configuration, establishes connections to databases and caches, and begins serving requests. No shared memory, no file system, no centralized metadata.

Data Flow and State Management

Each service instance maintains its own in-memory state, transaction logs, and caches. State is not shared across instances. When a request arrives, the service processes it using only its local state and external services.

For example, an order service receives a POST /orders request:

POST /orders HTTP/1.1
Host: orders.example.com
Content-Type: application/json

{
  "customer_id": 456,
  "items": [
    { "product_id": 101, "quantity": 2 },
    { "product_id": 102, "quantity": 1 }
  ]
}

The service:

  1. Reads the config.json and DATABASE_URL.
  2. Connects to the PostgreSQL database at db.example.com.
  3. Writes the order to the database using a transaction.
  4. Logs the operation to a local file and to a structured log stream.
  5. Updates a local in-memory cache of customer data.
  6. Responds with 201 Created and the new order ID.

No other instance knows about this order until it reads from the database or consumes a message.

Failure Modes and Silent Pitfalls

Shared Nothing Architecture reveals subtle failure modes that are often missed during development.

1. Stale Cache and Inconsistent Reads

When a service instance updates its local cache, other instances may serve stale data. If a customer's address is updated, only the instance that handles the update sees the change until it renews the cache.

This leads to:

To mitigate, use cache invalidation strategies:

2. Database Connection Leaks and Pooling Issues

Each service instance maintains its own database connection pool. If the pool is too small, new requests block. If too large, the database becomes overwhelmed.

A common failure is a Connection limit exceeded error from PostgreSQL:

ERROR:  too many connections (max 100)
CONTEXT:  SQL statement "SELECT * FROM orders WHERE customer_id = $1"

This occurs when:

Solution: use connection pooling (e.g., pgBouncer), and monitor connection counts via pg_stat_activity.

3. Clock Drift and Event Ordering

With no shared time reference, each instance may have slightly different clocks. This affects:

For example, a payment service receives a PaymentReceived event:

{
  "event_id": "pay-789",
  "payment_id": 1001,
  "timestamp": "2024-04-05T12:34:56.789Z",
  "amount": 150.00
}

If two instances process the same event at different times due to clock skew, they may both apply the payment, leading to double charges.

Solution: use event_id as a primary key and implement idempotency checks using a database table:

CREATE TABLE event_logs (
  event_id TEXT PRIMARY KEY,
  processed_at TIMESTAMP WITH TIME ZONE,
  status TEXT
);

Before processing, check if the event_id exists. If so, skip processing.

4. No Automatic Failover for Stateful Services

A service instance can fail and leave its state behind. If it maintains a local cache or logs, those are lost when the instance dies.

For example, a service instance running on a VM updates its in-memory cache and then crashes. The next instance to handle requests must:

This results in:

To prevent this, ensure all state is persisted to durable storage:

Scaling and Autonomy

Shared Nothing Architecture enables independent scaling of services. Each instance scales independently, and the system can tolerate instance churn.

Scale up by:

Scale out using a service discovery mechanism (e.g., DNS, Consul, Kubernetes).

Example: deploy 10 order service instances behind a load balancer.


env \
  DATABASE_URL="postgres://app_user:[email protected]:5432/orders" \
  LOG_LEVEL="info" \
  NODE_ID="order-$(hostname)" \
  /app/order-service --config config.json

The load balancer distributes requests across the 10 instances. Each instance:

When one instance fails:

Operational Observability

Monitoring and debugging are essential. Without shared state, observability must be distributed.

Use structured logging with consistent fields across services:

{
  "timestamp": "2024-04-05T12:34:56.789Z",
  "level": "info",
  "service": "order-service",
  "instance_id": "order-123",
  "trace_id": "a1b2c3d4e5f6",
  "event": "order_created",
  "data": {
    "order_id": 9876,
    "customer_id": 456,
    "total": 150.00
  }
}

Use distributed tracing with trace_id to correlate logs across services.

For example, a user checkout flow:

Each service includes the trace_id in logs and HTTP headers.

GET /checkout HTTP/1.1
Host: api.example.com
X-Trace-ID: a1b2c3d4e5f6

Logs from the order service include:

2024-04-05T12:34:56.789Z | info | order-service | order-123 | a1b2c3d4e5f6 | order_created | {"order_id": 9876, "customer_id": 456, "total": 150.00}

This allows full traceability across services, even with no shared state.

Common Pitfalls and Best Practices

1. Configuration Drift

Instances may have slightly different configurations due to manual updates or deployment scripts.

Detect drift with:

Example: a health check endpoint /health returns:

{
  "status": "healthy",
  "config": {
    "listen": "0.0.0.0:8080",
    "database": {
      "host": "db.example.com",
      "port": 5432
    },
    "cache": {
      "type": "redis"
    }
  },
  "uptime_seconds": 3421,
  "connected_to_database": true,
  "connected_to_cache": true
}

A misconfigured instance may report cache.type: "memcached" while others use redis.

2. Idempotency and Duplicate Processing

Because services are independent, duplicate events and requests are common.

Use:

Example: a POST /orders request with idempotency key:

POST /orders HTTP/1.1
Host: orders.example.com
Idempotency-Key: 12345
Content-Type: application/json

{
  "customer_id": 456,
  "items": [ ... ]
}

The service checks the idempotency_key table:

CREATE TABLE idempotency_keys (
  idempotency_key TEXT PRIMARY KEY,
  result_status TEXT,
  result_payload JSONB,
  created_at TIMESTAMP WITH TIME ZONE
);

If the key exists, return the stored result; otherwise, process the request and store the result.

3. Startup Time and Warmup

New instances take time to warm up: loading cache, connecting to databases, and building in-memory structures.

Reduce startup time by:

Example: after deployment, run:

curl -X POST http://order-service/health/preheat

This endpoint:

Summary

Shared Nothing Architecture is not a choice but a commitment to autonomy, resilience, and scalability. Each service instance is a self-contained unit that requires no shared state. It shines under high load, failure, and independent deployment. But its success depends on:

When done well, it delivers a system that scales effortlessly, fails gracefully, and evolves independently—without a single point of failure.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.