← Back to writing

How to Build Python Microservices With Nameko

How to Build Python Microservices With Nameko

Nameko handles RPC over AMQP, stateless workers, and automatic service discovery out of the box. This tutorial builds a complete Python microservices system — Gateway, Airports, and Trips — using RabbitMQ and Docker.

Mayur Domadiya · June 13, 2026 · 12 min read

Most Python microservices tutorials reach for Flask or FastAPI immediately. That works until you hit the part where services need to talk to each other at scale — then you're wiring up HTTP clients, managing retries, and debugging service discovery yourself. Nameko takes a different path. It ships RPC over AMQP built in, which means your services communicate through a RabbitMQ message broker rather than direct HTTP calls. You get automatic service discovery, stateless concurrency via eventlet greenthreads, and horizontal scaling that requires no configuration changes — just start another instance. This tutorial walks through Nameko from zero: a Hello World service, the concurrency model, horizontal scaling with multiple instances, and a full three-service travel application (Gateway, Airports, Trips) deployed with docker-compose. Every code block is runnable.

Why Microservices — and Why Nameko?

Martin Fowler's canonical definition of microservices describes an approach where a single application is built as a suite of small services, each running in its own process and communicating with lightweight mechanisms. The important structural consequence is that each service has its own lifecycle: it can be deployed, scaled, and updated independently. That independence is the architectural payoff.

Most microservices implementations use HTTP and REST for inter-service communication. Nameko uses RPC over AMQP (Advanced Message Queuing Protocol) via RabbitMQ instead. This is the "smart endpoints and dumb pipes" principle applied concretely: the message broker handles routing, queuing, and reliability, while the services contain all the business logic. A service does not need to know which other services are currently running or where they are located — Nameko handles discovery automatically through RabbitMQ.

Nameko services are Python classes. Entry points are defined through extensions — decorators that register methods as RPC endpoints, HTTP handlers, event listeners, or timers. Community extensions add integrations for PostgreSQL (via SQLAlchemy), Redis, and other dependencies through dependency injection. Nameko instantiates the relevant extension objects per worker automatically.

Setting Up the Environment

You need Docker and Python 3 installed. Create a virtual environment, then install Nameko:

pip install nameko

Nameko requires a RabbitMQ message broker. The simplest way to run one locally is with Docker — no local install, no daemon, stop the container when done:

docker run -p 5672:5672 --hostname nameko-rabbitmq rabbitmq:3

This starts a RabbitMQ container on the default AMQP port (5672). Nameko services will automatically connect to it when you run them on the same machine.

Your First Nameko Service: Hello World

Create a file called hello.py. A Nameko service is a Python class with a name attribute and at least one entry point. The @rpc decorator registers a method as an RPC endpoint:

from nameko.rpc import rpc

class GreetingService:
    name = "greeting_service"

    @rpc
    def hello(self, name):
        return "Hello, {}!".format(name)

Start the service with nameko run hello. Then open a second terminal and launch the Nameko interactive shell:

nameko shell

The shell exposes an n object that lets you make RPC calls and dispatch events. Call the greeting service:

>>> n.rpc.greeting_service.hello(name='world')
'Hello, world!'

Nameko found the service through RabbitMQ automatically — no service registry, no URL configuration. That is the discovery model: any service connected to the same RabbitMQ instance is reachable by name.

Not sure where to start with AI?

Book a free 20-minute AI Feature Scoping Call. We'll map your highest-ROI AI feature, tell you the real cost, and whether Boundev is the right fit. No decks. No BS.

Book scoping call →

Workers, Concurrency, and the Stateless Design

Each Nameko service is instantiated when a call arrives and destroyed when the call completes. This means the service must be stateless: no instance variables, no class-level state that persists between calls. This is a constraint by design. Stateless services allow Nameko to run multiple concurrent workers without any coordination overhead.

Nameko uses eventlet greenthreads for concurrency. By default, up to 10 workers can run simultaneously. To see this in practice, add a blocking sleep to the handler — Nameko will patch the sleep call automatically to yield to other greenthreads:

from time import sleep
from nameko.rpc import rpc

class GreetingService:
    name = "greeting_service"

    @rpc
    def hello(self, name):
        sleep(5)
        return "Hello, {}!".format(name)

A single call now takes 5 seconds. But 5 concurrent calls still finish in around 5 seconds, not 25 — because each worker starts immediately and blocks independently. Use call_async for non-blocking RPC calls that return a proxy object you can query for results later:

>>> res = []
>>> for i in range(5):
...     hello_res = n.rpc.greeting_service.hello.call_async(name=str(i))
...     res.append(hello_res)
>>> for hello_res in res:
...     print(hello_res.result())
Hello, 0!
Hello, 1!
Hello, 2!
Hello, 3!
Hello, 4!

The result() call blocks until the response arrives. Five workers each sleep 5 seconds concurrently, so the total wall-clock time is still ~5 seconds. Replace the sleep with a real database call and this model handles I/O-bound workloads efficiently.

Scaling Horizontally: Running Multiple Instances

When all 10 default workers are busy, new requests queue in RabbitMQ until a worker frees up. The solution is not increasing the per-instance worker limit — it's starting another service instance. Nameko distributes RPC calls round-robin across all running instances of the same service automatically.

Nameko services are stateless by design — and that constraint is exactly what makes horizontal scaling trivial.

To test this, run a second terminal and start the service again with nameko run hello. Two instances now run, each handling up to 10 concurrent workers — 20 total. Send 20 concurrent calls with range(20) and the full batch completes in ~5 seconds again instead of ~10.

Nameko also handles instance failures gracefully. If you kill one terminal mid-run (Ctrl+C twice), calls in-flight on that instance are reallocated to remaining instances. In production, Docker containerizes each service instance and Kubernetes manages the cluster — adding or removing instances without downtime. During rolling deploys, multiple versions of the same service can run simultaneously. Nameko routes calls round-robin across all instances regardless of version, so backward compatibility across API versions is essential.

Building a Full System: Gateway, Airports, and Trips

The real value of Nameko becomes clear in a multi-service system. This example builds a minimal travel application: airports can be registered and retrieved, trips record origin-destination pairs. Three services handle the work — Gateway exposes HTTP endpoints, Airports and Trips handle data storage via RPC.

The Gateway service exposes HTTP endpoints using the @http decorator and delegates all data operations to Airports and Trips via RPC proxies:

import json
from nameko.rpc import RpcProxy
from nameko.web.handlers import http

class GatewayService:
    name = 'gateway'

    airports_rpc = RpcProxy('airports_service')
    trips_rpc = RpcProxy('trips_service')

    @http('GET', '/airport/<string:airport_id>')
    def get_airport(self, request, airport_id):
        airport = self.airports_rpc.get(airport_id)
        return json.dumps({'airport': airport})

    @http('POST', '/airport')
    def post_airport(self, request):
        data = json.loads(request.get_data(as_text=True))
        airport_id = self.airports_rpc.create(data['airport'])
        return airport_id

    @http('GET', '/trip/<string:trip_id>')
    def get_trip(self, request, trip_id):
        trip = self.trips_rpc.get(trip_id)
        return json.dumps({'trip': trip})

    @http('POST', '/trip')
    def post_trip(self, request):
        data = json.loads(request.get_data(as_text=True))
        trip_id = self.trips_rpc.create(
            data['airport_from'], data['airport_to']
        )
        return trip_id

Notice that Gateway never imports Airports or Trips directly — it only holds RPC proxy objects. The dependency is on the service name string, not on a module. This is the decoupling that lets each service be deployed and updated independently.

The Airports service exposes two RPC methods and uses Redis for storage through the nameko_redis community extension. Nameko injects the Redis connection as a dependency — the service class does not instantiate it:

import uuid
from nameko.rpc import rpc
from nameko_redis import Redis

class AirportsService:
    name = "airports_service"

    redis = Redis('development')

    @rpc
    def get(self, airport_id):
        return self.redis.get(airport_id)

    @rpc
    def create(self, airport):
        airport_id = uuid.uuid4().hex
        self.redis.set(airport_id, airport)
        return airport_id

The Trips service follows the same pattern. Each Dockerfile is standard: FROM python:3, copy requirements, run pip install, copy source, set a startup script as CMD. The startup script waits for RabbitMQ and Redis to be ready before starting the service. With all three services defined, bring the full system up:

docker-compose up

Test the system end-to-end with curl:

# Create airport
curl -i -d '{"airport": "first_airport"}' localhost:8000/airport
# Returns: f2bddf0e506145f6ba0c28c247c54629

# Verify it stored correctly
curl localhost:8000/airport/f2bddf0e506145f6ba0c28c247c54629
# {"airport": "first_airport"}

# Create a trip between two airports
curl -i -d '{"airport_from": "f2bddf0...", "airport_to": "565000ad..."}' localhost:8000/trip
# Returns: 34ca60df07bc42e88501178c0b6b95e4

The Gateway receives the HTTP request, calls Airports or Trips over RPC, those services read and write Redis, and the response flows back through the Gateway. The whole chain runs through RabbitMQ without any direct network addresses between services.

What This Means

Nameko's design choices have a coherent logic. Stateless workers enable concurrency without shared memory problems. RPC over AMQP enables loose coupling without service registration overhead. Automatic round-robin distribution enables horizontal scaling without a load balancer configuration. Each constraint is a feature.

For production use, the workflow points toward Kubernetes for orchestration — it handles rolling deploys, node scaling, and instance health management. The Nameko-specific consideration in that context is backward compatibility: during a rolling deploy, multiple service versions run simultaneously. Nameko distributes calls across all of them. If a new version changes an RPC method signature in a way that breaks older callers, the deploy creates an error window. Design API changes additively — add parameters with defaults before removing old ones.

Microservices architecture is foundational to any AI-powered backend that needs to separate concerns cleanly: an inference endpoint, a data pipeline, a feature store, a monitoring service, and a user-facing API can all run as independent Nameko services, each scaling to the demand profile of its specific workload. The AI engineering work that connects those services — managing data flow, versioning models, and monitoring inference quality — is where architecture choices made at the framework selection stage compound over time. Nameko's RabbitMQ-backed model removes a class of coordination problems before they start.

Got an AI feature in mind?

Book a free 20-minute AI Feature Scoping Call. We'll tell you whether Boundev is the right fit, what tier you'd need, and how fast we can ship. We say no to about a third of calls — the fit either works or it doesn't.

Book scoping call →
MD

Mayur Domadiya

Founder & CEO, Boundev AI

Mayur builds Boundev AI, the AI engineering subscription for US SaaS companies. Connect on Twitter or LinkedIn.

Get shipped

Rather we just build it?

Book a free scoping call and we'll ship your production-safe AI feature this week.