System Design

Building a High-Performance Communications Gateway with Go and Azure Functions

How I achieved sub-millisecond latency and 80% bandwidth reduction using a custom Go handler on Azure Functions.

November 24, 2025
3 min read
Building a High-Performance Communications Gateway with Go and Azure Functions

Introduction

Serverless functions are often associated with Node.js or Python, but for high-performance workloads, Go is an incredible choice. I recently architected a unified communications gateway—handling OTPs, SMS, and Email—that leverages Go as a Custom Handler on Azure Functions.

The result? A production-ready microservice with sub-millisecond middleware overhead and enterprise-grade security.

The Architecture

I needed a system that could handle bursty traffic (like marketing campaigns) without breaking the bank, while maintaining strict security standards.

1. The “Custom Handler” Pattern

Azure Functions allows you to run any executable as a function handler. I compiled the Go application into a single binary. This gives the raw performance of Go while keeping the serverless benefits of auto-scaling and pay-per-execution.

2. The 9-Layer Middleware Stack

Security and observability weren’t afterthoughts; they were baked into the request pipeline. Every request passes through 9 layers of middleware:

  1. Security Headers: Automated XSS and HSTS protection.
  2. Compression: Gzip with writer pooling (more on this below).
  3. Request Tracing: UUID generation for distributed tracing.
  4. Metrics: Real-time stats collection.
  5. Rate Limiter: Token bucket algorithm to prevent abuse.
  6. Auth: API Key validation.
  7. Recovery: Panic recovery to prevent crashes from taking down the instance.
  8. Logger: Structured logging.
  9. Size Limit: Preventing DoS via large payloads.

3. Hybrid Cloud Strategy

I didn’t limit the architecture to a single cloud provider.

  • Compute & DB: Azure Functions + Cosmos DB (for its global distribution).
  • Delivery: AWS SES (Email) and Pinpoint (SMS) for their superior deliverability rates.
graph TD
    Client[Client App] -->|HTTPS| AZ[Azure Function]
    
    subgraph "Go Custom Handler"
        MW[Middleware Pipeline]
        Router[Router]
        
        MW -->|Valid| Router
        MW -->|Invalid| Err[Error Response]
        
        Router --> H_OTP[OTP Handler]
        Router --> H_SMS[SMS Handler]
        Router --> H_Email[Email Handler]
    end
    
    AZ --> MW
    
    H_OTP -->|Store| Cosmos[Azure Cosmos DB]
    H_OTP -->|Send| SES[AWS SES]
    H_SMS -->|Send| Pinpoint[AWS Pinpoint]
    H_Email -->|Send| SES

Optimizing Binary Size

In serverless, cold start times are directly related to binary size. I employed aggressive optimization strategies to keep the Go binary under 10MB.

Build Flags

I use specific linker flags to strip debugging information and symbol tables:

# Makefile
build-azure-optimized:
	GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
	go build \
	-ldflags="-s -w" \
	-trimpath \
	-o bin/api cmd/api/main.go
  • -s: Omit the symbol table and debug information.
  • -w: Omit the DWARF symbol table.
  • -trimpath: Removes file system paths from the compiled executable.

UPX Compression

For the final mile, I use UPX (Ultimate Packer for eXecutables). This compresses the binary at rest and decompresses it in memory at runtime.

upx --best --lzma bin/api

This combination reduced the final binary size by over 60%, resulting in significantly faster cold starts.

Performance Wins

Writer Pooling for Gzip

One of the biggest optimizations was in the compression middleware. Instead of allocating a new Gzip writer for every request, I implemented a sync.Pool. This reduced memory allocations significantly and cut bandwidth usage by 80% for large JSON responses.

Connection Pooling

I implemented robust connection pooling for both Cosmos DB and AWS clients. In a serverless environment, re-establishing SSL connections on every invocation is a performance killer. By maintaining a global client instance, connections stay alive across warm starts.

Conclusion

By choosing Go and the Custom Handler pattern, I built a service that is fast, secure, and incredibly cost-effective. It demonstrates that serverless doesn’t have to mean compromising on performance or control.

Tags

#Go #Azure Functions #Serverless #Performance #Middleware
Rommel

Written by Rommel Saquicela

Senior Tech Lead & Solutions Architect

View Profile