How nanoservices work
When one nanoservice calls another, the callee runs in the same thread and process. This provides:- Microservice isolation: Components are decoupled and independently deployable
- Local performance: No network overhead between services
- Capability-based security: Services connect through explicit bindings rather than global namespaces
Deployment model
Instead of deploying different microservices to different machines in your cluster, deploy all your nanoservices to every machine in the cluster. This approach:- Simplifies load balancing significantly
- Eliminates network hops between services
- Maintains service isolation and independent deployment
Configuration
Nanoservices are defined in your workerd config file using service bindings. Here’s an example:Using service bindings
In your main worker, call other services using thefetch() API:
The URL passed to
fetch() is used for routing within the service but doesn’t result in an actual network request.Capability bindings
workerd uses capability-based security instead of global namespaces to connect services. Each service explicitly declares its dependencies through bindings in the config file. This approach:- Makes code more composable
- Prevents SSRF (Server-Side Request Forgery) attacks
- Provides clear dependency graphs
- Enables fine-grained access control
Benefits
Performance
In-process calls eliminate network latency between services
Isolation
Each service runs in its own JavaScript isolate with separate memory
Security
Capability-based bindings prevent unauthorized access
Simplicity
Homogeneous deployment simplifies infrastructure
Comparison to microservices
| Aspect | Microservices | Nanoservices |
|---|---|---|
| Deployment | Different services on different machines | All services on every machine |
| Communication | Network calls (HTTP/gRPC) | In-process function calls |
| Latency | Milliseconds (network overhead) | Microseconds (local call) |
| Load balancing | Complex (service discovery required) | Simple (every machine has all services) |
| Isolation | Process/container boundaries | V8 isolate boundaries |
| Resource usage | Higher (separate processes) | Lower (shared process) |
Best practices
- Keep services focused: Each nanoservice should have a single, well-defined responsibility
- Use explicit bindings: Declare all service dependencies in your config file
- Version carefully: Changes to service interfaces may require coordinated deployments
- Monitor per-service: Track metrics for each nanoservice independently
- Test in isolation: Each service should have its own test suite