Mailer-Go: SMS Gateway

Dec 30, 2025 4 mins read


Source Code: github.com/josnelihurt/mailer-go

Overview

An SMS-to-Email gateway system that enables receiving text messages from anywhere in the world without roaming charges. The system uses GSM modems connected to LXC containers, processes incoming SMS, and forwards them via email while publishing to Redis Pub/Sub for workflow automation.

Motivation

Traveling internationally or working remotely often requires receiving SMS messages for two-factor authentication, service notifications, or important communications. Traditional solutions involve expensive roaming fees, managing multiple SIM cards, or dealing with the complexity of VPN tunneling for remote device access.

I needed a reliable way to receive SMS messages from my phone numbers regardless of where I was physically located. Initial attempts using VPN solutions (WireGuard, ZeroTier, Tailscale) to tunnel USB devices proved problematic due to latency, connection stability issues, and unnecessary complexity for the simple task of forwarding text messages.

The solution evolved into a lightweight architecture: LXC containers with direct USB passthrough for GSM modems, combined with a Go microservice that receives SMS data over HTTPS and publishes to Redis for downstream processing.

Technical Stack

Client Side (LXC Containers):

  • SMSTools3 - SMS gateway daemon for GSM modem communication
  • Go 1.20 - Native compiled binaries for file watching and SMS processing
  • Proxmox LXC - Lightweight containers with USB passthrough
  • systemd - Service management for the file watcher daemon

Server Side (VPS):

  • Go 1.20 - HTTP API server
  • Docker - Container runtime
  • Redis - Pub/Sub message bus
  • Traefik - Reverse proxy with automatic HTTPS

Deployment:

  • Ansible - Remote configuration and deployment automation
  • rsync - Efficient code synchronization
  • Bash - Deployment scripts organized by target server

Architecture

The system operates in two modes: client and server.

Client Mode (File Watcher)

LXC containers run on a Proxmox host with GSM modems attached via USB passthrough. Each container independently processes SMS from one modem:

  1. SMSTools3 detects incoming SMS and writes files to /var/spool/sms/incoming
  2. Mailer-Go client watches that directory using filesystem events
  3. On new file detection:
    • Parses SMSTools3 format into structured JSON
    • Sends email notification via Gmail SMTP
    • POSTs the message to the HTTP API server
    • Moves processed file to /var/spool/sms/done or /var/spool/sms/err

Server Mode (HTTP API)

A Docker container running on a VPS exposes an authenticated HTTP API:

  1. Receives POST /v1/sms/enqueue requests with SMS data
  2. Validates API key via X-API-Key header
  3. Publishes structured JSON message to Redis Pub/Sub channel sms:incoming
  4. Returns success response to client

This design enables real-time SMS processing through n8n workflows, Telegram bots, or custom automation that subscribes to the Redis channel.

What I Learned

This project reinforced several infrastructure lessons:

LXC vs VPNs for Hardware Access

  • USB passthrough in LXC containers provides direct, reliable hardware access
  • VPN tunneling introduces latency, complexity, and connection stability issues
  • For simple data forwarding, an HTTPS API is more maintainable than tunneled device access

AppArmor and Docker in LXC

  • Docker build operations failed in LXC due to AppArmor restrictions
  • Native Go compilation proved simpler and more reliable than containerization
  • LXC containers are lightweight enough that Docker adds unnecessary overhead for simple services

Deployment Optimization

  • Initial Docker image transfers (32.5 MB) were slow over limited bandwidth connections
  • Switching to rsync with native compilation reduced transfers to ~192 KB
  • Remote compilation on target servers eliminates image transport entirely

Message Structure Evolution

  • Started with raw string message passing
  • Refactored to structured JSON with SMSMessage type for better downstream integration
  • Added smstools_file field as fallback for parsing failures
  • Proper message contracts simplify integration with n8n and other workflow tools

Redis Lists vs Pub/Sub

  • Initially implemented with Redis Lists (LPush)
  • Migrated to Pub/Sub when integrating with n8n (Redis Trigger node requires Pub/Sub)
  • Pub/Sub enables multiple consumers and real-time processing patterns
  • See Redis Lists vs Pub/Sub: When to Use Each for detailed comparison

Future Enhancements

The current implementation handles incoming SMS reliably. Planned expansions include:

n8n Integration (in progress)

  • Workflow automation triggered by Redis Pub/Sub messages
  • Forward SMS to Telegram channels
  • Database storage for message history
  • Custom triggers based on sender or keywords

Two-Way SMS (planned)

  • API endpoint for sending outbound SMS
  • Integration with SMSTools3 outgoing queue
  • Delivery status tracking
  • Telegram bot interface for sending replies

Observability (planned)

  • Prometheus metrics for message throughput and error rates
  • Grafana dashboards for system health monitoring
  • Alert integration for delivery failures