Skip to main content
Version: 1.40

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
FieldDescription
driverThe backend for divert routing. Options: nginx (default) or istio
namespaceThe shared namespace containing the full application stack

When you run okteto deploy, Okteto automatically:

  1. Deploys only the services defined in your manifest
  2. Configures routing to redirect requests for missing services to the shared namespace
  3. 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:

  1. Your personal endpoint: https://movies-alice.okteto.example.com
  2. Or the shared endpoint with your header: baggage: okteto-divert=alice

Troubleshooting

This section covers common issues developers encounter when using Divert. For admin-level troubleshooting, see Configure Divert.

Verify Sidecar Injection (nginx driver)

Check if Linkerd sidecar is running:

kubectl get pods -n <your-namespace> -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].name}{"\n"}{end}'

Should show both your application container and linkerd-proxy.

If sidecar is missing:

# Check namespace annotation
kubectl get namespace <your-namespace> -o jsonpath='{.metadata.annotations}'

# Restart deployment to trigger injection
kubectl rollout restart deployment/<your-deployment> -n <your-namespace>

Check Ingress Controller

Verify ingress is created:

kubectl get ingress -n <your-namespace>

Should show your service ingress with divert annotations.

Check ingress controller logs:

kubectl logs -n okteto deployment/okteto-nginx -f | grep <your-namespace>

Verify Linkerd Deployment (nginx driver)

Check Linkerd control plane:

# Quick health check
linkerd check

# Or manually verify deployments
kubectl get deployments -n linkerd

Watch traffic flow:

linkerd viz tap deployment/<your-deployment> --namespace <your-namespace>

Header Propagation Issues

Test header at ingress:

curl -v https://your-app.okteto.example.com 2>&1 | grep -i baggage
# Should see: baggage: okteto-divert=<your-namespace>

Verify header format:

# ✅ Correct
baggage: okteto-divert=alice-feature

# ❌ Incorrect
baggage.okteto-divert=alice-feature
okteto-divert: alice-feature

Check application code:

  • Ensure all services propagate the baggage header to downstream calls
  • Add logging at service boundaries: console.log('Baggage header:', req.headers['baggage'])
  • Verify header exists in: HTTP clients, gRPC calls, message queue producers

Test service-to-service routing:

kubectl exec -it deployment/<your-service> -n <your-namespace> -- \
curl -H "baggage: okteto-divert=<your-namespace>" \
http://api-service/endpoint

Traffic Not Being Diverted

  1. Check header format: Ensure you're using baggage: okteto-divert=<namespace> (not baggage.okteto-divert)
  2. Verify header propagation: All services in the call chain must forward the baggage header
  3. Check namespace name: The namespace in the header must match your Okteto namespace exactly
  4. Verify divert config: In your okteto.yaml:
    deploy:
    divert:
    namespace: staging # Must match shared environment
    driver: nginx # Must match cluster config

Services Not Discovered

  1. Verify shared namespace: Ensure the shared namespace is running and healthy

    kubectl get pods -n staging
    # All pods should be Running
  2. Check service names: Service discovery uses Kubernetes DNS

    # Services are accessible at:
    <service-name>.<namespace>.svc.cluster.local

    # Test DNS resolution:
    kubectl exec -it deployment/<your-service> -- \
    nslookup api-service.staging.svc.cluster.local
  3. Review divert config: Ensure divert.namespace points to the correct shared environment

  4. Check network policies:

    kubectl get networkpolicies -n staging
    kubectl get networkpolicies -n <your-namespace>
    # Ensure cross-namespace communication is allowed

Database Connection Issues

  1. Check connection strings: Ensure they resolve to the correct database (shared vs. local)

    # For shared database:
    postgresql.staging.svc.cluster.local

    # For local database:
    postgresql.<your-namespace>.svc.cluster.local
  2. Verify network policies: Ensure cross-namespace communication is allowed

    # Test connectivity
    kubectl exec -it deployment/<your-service> -n <your-namespace> -- \
    nc -zv postgresql.staging.svc.cluster.local 5432
  3. Test connectivity: Use kubectl exec to test database connectivity from your pod

    kubectl exec -it deployment/<your-service> -n <your-namespace> -- \
    psql -h postgresql.staging.svc.cluster.local -U user -d database

Quick Diagnostic Checklist

Run through this checklist when troubleshooting:

  • Linkerd sidecars are running in your pods (nginx driver)
  • Ingress is created with correct annotations
  • Shared namespace exists and all services are Running
  • Header format is correct: baggage: okteto-divert=<namespace>
  • Application code propagates headers to all downstream calls
  • DNS resolves shared services: <service>.staging.svc.cluster.local
  • Network policies allow cross-namespace traffic
  • divert.namespace in okteto.yaml matches shared environment

Getting Additional Help

If issues persist:

  1. Check pod logs:

    kubectl logs deployment/<your-deployment> -n <your-namespace>
  2. Review pod events:

    kubectl describe pod <pod-name> -n <your-namespace>
  3. Consult related documentation:

Best Practices

  1. Name namespaces descriptively: Use patterns like <username>-<feature> for clarity
  2. Clean up when done: Delete personal namespaces after completing work
  3. Keep shared environment updated: Regularly deploy updates to the shared staging environment
  4. Document header propagation: Ensure all team members understand which headers to propagate
  5. Use environment variables: Reference shared namespace via variables for flexibility
  6. Monitor resource usage: Track namespace quotas and clean up unused resources

Next Steps

Example Repositories