Using Divert
This guide covers the practical implementation of Divert in your Okteto development environments, including manifest configuration, header propagation, and common patterns for databases and message queues.
Okteto Manifest Configuration
The divert section goes under deploy in your okteto.yaml to configure traffic routing between your development environment and a shared namespace.
Basic Divert Configuration (nginx driver)
deploy:
commands:
- helm upgrade --install myservice chart --set image=${OKTETO_BUILD_IMAGE}
divert:
driver: nginx # Optional, nginx is the default
namespace: staging
| Field | Description |
|---|---|
driver | The backend for divert routing. Options: nginx (default) or istio |
namespace | The shared namespace containing the full application stack |
When you run okteto deploy, Okteto automatically:
- Deploys only the services defined in your manifest
- Configures routing to redirect requests for missing services to the shared namespace
- Injects the
baggage: okteto-divert=<your-namespace>header into requests through your endpoints
Istio Driver Configuration
If your cluster uses Istio for service mesh, use the istio driver:
deploy:
commands:
- helm upgrade --install myservice chart --set image=${OKTETO_BUILD_IMAGE}
divert:
driver: istio
virtualServices:
- name: frontend-vs
namespace: staging
routes:
- route-to-frontend
hosts:
- virtualService: frontend
namespace: staging
For complete configuration details, see the manifest reference.
Project Structure Patterns
Single Service Development
For working on a single service, create a dedicated manifest:
# okteto.frontend.yaml
build:
frontend:
context: frontend
deploy:
commands:
- helm upgrade --install frontend chart/frontend --set image=${OKTETO_BUILD_FRONTEND_IMAGE}
divert:
namespace: ${OKTETO_SHARED_NAMESPACE:-staging}
Multi-Service Development
When working on related services together:
# okteto.rentals.yaml
build:
rent:
context: rentals
worker:
context: worker
deploy:
commands:
- helm upgrade --install rent chart/rent --set image=${OKTETO_BUILD_RENT_IMAGE}
- helm upgrade --install worker chart/worker --set image=${OKTETO_BUILD_WORKER_IMAGE}
- helm upgrade --install kafka chart/kafka
- helm upgrade --install postgresql chart/postgresql
divert:
namespace: ${OKTETO_SHARED_NAMESPACE:-staging}
Using Environment Variables
Reference the shared namespace via environment variable for flexibility:
export OKTETO_SHARED_NAMESPACE="movies-shared"
okteto deploy -f okteto.frontend.yaml
Header Propagation
For Divert to work across your service mesh, implement header propagation in your services. The baggage header must be extracted from incoming requests and included in all outgoing requests.
JavaScript/Node.js (Express)
const express = require('express');
const axios = require('axios');
const app = express();
// Middleware to capture baggage header
app.use((req, res, next) => {
req.baggage = req.headers['baggage'] || '';
next();
});
// Propagate in outgoing requests
app.get('/api/movies', async (req, res) => {
const response = await axios.get('http://catalog:8080/movies', {
headers: { 'baggage': req.baggage }
});
res.json(response.data);
});
Go
package main
import (
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
baggage := r.Header.Get("baggage")
// Create downstream request
req, _ := http.NewRequest("GET", "http://catalog:8080/movies", nil)
req.Header.Set("baggage", baggage)
client := &http.Client{}
resp, _ := client.Do(req)
// Handle response...
}
Java/Spring Boot
@RestController
public class ApiController {
private final WebClient webClient;
@GetMapping("/api/movies")
public Mono<Movies> getMovies(@RequestHeader(value = "baggage", required = false) String baggage) {
return webClient.get()
.uri("http://catalog:8080/movies")
.header("baggage", baggage != null ? baggage : "")
.retrieve()
.bodyToMono(Movies.class);
}
}
Python (FastAPI)
from fastapi import FastAPI, Request
import httpx
app = FastAPI()
@app.get("/api/movies")
async def get_movies(request: Request):
baggage = request.headers.get("baggage", "")
async with httpx.AsyncClient() as client:
response = await client.get(
"http://catalog:8080/movies",
headers={"baggage": baggage}
)
return response.json()
Database Isolation Patterns
When using Divert, you can choose between shared or isolated databases depending on your needs.
Shared Database (Default)
Services connect to the database in the shared namespace. This is the simplest approach and works well when you don't need to modify the database schema or data:
# Your diverted service uses the shared database
env:
- name: DATABASE_URL
value: postgresql://postgres:5432/movies # Resolves to shared namespace
Isolated Database per Developer
Deploy your own database instance when you need isolation for schema changes or test data:
deploy:
commands:
- helm upgrade --install mongodb chart/mongodb # Local database
- helm upgrade --install catalog chart/catalog --set image=${OKTETO_BUILD_IMAGE}
divert:
namespace: staging
Your service then connects to the local database:
env:
- name: MONGODB_URL
value: mongodb://mongodb:27017/catalog # Local instance
Message Queue Routing Patterns
For queue-based systems, you can route messages based on the baggage header to ensure proper service isolation.
SQS Queue Routing
When publishing messages, include the namespace in the message attributes:
// Producer: Include routing info in message
const baggage = req.headers['baggage'] || '';
const namespace = extractNamespace(baggage); // Extract from "okteto-divert=namespace"
await sqs.sendMessage({
QueueUrl: QUEUE_URL,
MessageBody: JSON.stringify(orderData),
MessageAttributes: {
'okteto-namespace': {
DataType: 'String',
StringValue: namespace || 'shared'
}
}
});
Consumer filters messages by namespace:
// Consumer: Filter messages by namespace
const messages = await sqs.receiveMessage({
QueueUrl: QUEUE_URL,
MessageAttributeNames: ['okteto-namespace']
});
for (const message of messages.Messages) {
const targetNamespace = message.MessageAttributes?.['okteto-namespace']?.StringValue;
if (targetNamespace === CURRENT_NAMESPACE || targetNamespace === 'shared') {
// Process this message
await processOrder(JSON.parse(message.Body));
}
}
Kafka Topic Routing
Use message headers for Kafka routing:
// Producer
await producer.send({
topic: 'orders',
messages: [{
value: JSON.stringify(order),
headers: {
'okteto-namespace': namespace
}
}]
});
// Consumer
await consumer.run({
eachMessage: async ({ message }) => {
const targetNamespace = message.headers['okteto-namespace']?.toString();
if (targetNamespace === CURRENT_NAMESPACE || !targetNamespace) {
await processOrder(JSON.parse(message.value));
}
}
});
Testing Your Diverted Environment
Using curl
Test routing with the baggage header:
# Without header - uses shared services
curl https://movies-staging.okteto.example.com/api/catalog/healthz
# Response: {"status": "ok", "namespace": "staging"}
# With header - routes to your namespace
curl -H "baggage: okteto-divert=alice" \
https://movies-staging.okteto.example.com/api/catalog/healthz
# Response: {"status": "ok", "namespace": "alice"}
Using Browser Extensions
Install a header modification extension (like ModHeader) and add:
- Header Name:
baggage - Header Value:
okteto-divert=<your-namespace>
Automated Testing
Include header propagation in your test setup:
// Jest/Mocha test setup
const request = require('supertest');
describe('Catalog API', () => {
it('should return movies', async () => {
const response = await request(app)
.get('/api/movies')
.set('baggage', `okteto-divert=${process.env.OKTETO_NAMESPACE}`)
.expect(200);
expect(response.body.namespace).toBe(process.env.OKTETO_NAMESPACE);
});
});
Multi-Developer Collaboration
Accessing Another Developer's Environment
To test a colleague's changes, use their namespace in the baggage header:
curl -H "baggage: okteto-divert=bob-feature" \
https://movies-staging.okteto.example.com/api/movies
Sharing Your Work
Others can access your diverted environment using:
- Your personal endpoint:
https://movies-alice.okteto.example.com - Or the shared endpoint with your header:
baggage: okteto-divert=alice
Troubleshooting
Traffic Not Being Diverted
- Check header format: Ensure you're using
baggage: okteto-divert=<namespace>(notbaggage.okteto-divert) - Verify header propagation: All services in the call chain must forward the baggage header
- Check namespace name: The namespace in the header must match your Okteto namespace exactly
Services Not Discovered
- Verify shared namespace: Ensure the shared namespace is running and healthy
- Check service names: Service discovery uses Kubernetes DNS (
<service>.<namespace>.svc.cluster.local) - Review divert config: Ensure
divert.namespacepoints to the correct shared environment
Database Connection Issues
- Check connection strings: Ensure they resolve to the correct database (shared vs. local)
- Verify network policies: Ensure cross-namespace communication is allowed
- Test connectivity: Use
kubectl execto test database connectivity from your pod
Best Practices
- Name namespaces descriptively: Use patterns like
<username>-<feature>for clarity - Clean up when done: Delete personal namespaces after completing work
- Keep shared environment updated: Regularly deploy updates to the shared staging environment
- Document header propagation: Ensure all team members understand which headers to propagate
- Use environment variables: Reference shared namespace via variables for flexibility
- Monitor resource usage: Track namespace quotas and clean up unused resources
Next Steps
- Divert Core Concepts - Understanding Divert architecture
- Divert Tutorial - Step-by-step getting started guide
- Manifest Reference - Complete configuration options
- Example Repositories - Working code samples
Example Repositories
- Movies with Divert - Multi-service example
- TacoShop with Divert Queues - Queue routing patterns
- Divert with Istio Sample - Istio driver configuration