DEV Community

Yusuf Bender
Yusuf Bender

Posted on

Building a Dynamic Reverse Proxy with Go: Hot Reload, Load Balancing & CI/CD

Have you ever wanted to build your own reverse proxy from scratch, with all the flexibility of dynamic configuration, hot reload, and load balancing—without relying on Nginx or Traefik?

In this project, I built a fully working reverse proxy server in Go, supporting features like YAML-based configuration, basic authentication, path rewriting, round-robin routing, and live configuration reloads. It’s also fully containerized with Docker and tested using GitHub Actions.

Let me walk you through the highlights of this project and why it’s more than just a toy proxy.

What Is a Reverse Proxy?
A reverse proxy is a server that sits between clients and backend services, forwarding client requests to the appropriate internal services. It's used for load balancing, authentication, caching, rewriting URLs, and more. Think of it as your system’s traffic controller.

Why I Built My Own
Tools like Nginx and Caddy are excellent—but sometimes too abstract. I wanted to understand how things work under the hood: how a proxy handles routes, manages load balancing, or reloads configs without restarting. By building my own, I learned deeply about Go’s net/http, reverse proxying via httputil, and how to structure production-ready systems.

Features
This proxy isn’t just functional—it’s production-aware. Here’s what it includes:

Dynamic YAML configuration

Round-robin load balancing between multiple targets

Health checks via /health endpoints

Path rewriting (e.g., /api -> /user)

Basic Authentication for protected routes

Hot reload on config file changes (no need to restart the server)

Logging and rate limiting middleware

Unit tested with Go's testing package

CI pipeline using GitHub Actions for test + Docker build

**Project Structure
**Here’s how the project is organized:

main.go: Entry point with middleware setup and hot reload logic

router.go: Core reverse proxy logic, request handling, round-robin logic

router_test.go: Unit tests for health checks, load balancing, and rewrite logic

routes.yaml: Defines dynamic routing rules, targets, auth credentials

api-backend/: Sample API service for testing (written in Go, Dockerized)

.github/workflows/ci.yml: GitHub Actions config for CI pipeline

**Example Configuration (routes.yaml)
**The YAML file defines how incoming paths are routed to target servers, including optional authentication and rewrite paths. For example:

yaml
Kopyala
Düzenle
routes:

  • path: /api targets:
    • http://localhost:5001
    • http://localhost:5003 auth: username: admin password: 1234 rewrite: /user This means any request to /api will be routed in round-robin fashion to the targets, protected with basic auth, and the path will be rewritten to /user.

**Hot Reload in Action
**One of the biggest challenges was implementing a hot reload system that watches the config file for changes. If routes.yaml is updated, the server reloads routes without restarting. This mimics how systems like Traefik work and adds flexibility in dynamic environments.

**CI/CD Setup
**Every commit triggers the following steps via GitHub Actions:

Runs all Go unit tests

Builds the Docker image for the API backend

(Optional) Could be extended to push to Docker Hub or deploy to a staging environment

This ensures the proxy remains stable and buildable at all times.

**Lessons Learned
**Go’s net/http and httputil.ReverseProxy provide great building blocks for low-level HTTP control.

YAML makes dynamic configuration super clean for routing rules.

Hot reload can be implemented simply with file watchers and mutex locking.

Writing tests for a proxy server can be tricky, especially when simulating backend servers, but it's possible with httptest.

**What’s Next?
**Adding a Web UI dashboard to visualize logs, active routes, and traffic

Support for JWT authentication

Metrics support with Prometheus

Redis-backed caching layer

Live reload via SIGHUP signal or WebSocket interface

Conclusion
This project was both a systems exercise and a backend engineering challenge. If you're learning Go or preparing for DevOps roles, building something like this sharpens your skills in concurrency, testing, and real-world infrastructure patterns.

You can find the full code here:
GitHub Repo: github.com/yusufbender/bender-reverse-proxy

If you like the project, feel free to star it or fork and extend it!

Let me know what you think—or even better, contribute and build together 🚀

Top comments (0)