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:
- SMSTools3 detects incoming SMS and writes files to
/var/spool/sms/incoming
- Mailer-Go client watches that directory using filesystem events
- 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:
- Receives
POST /v1/sms/enqueue requests with SMS data
- Validates API key via
X-API-Key header
- Publishes structured JSON message to Redis Pub/Sub channel
sms:incoming
- 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
Related Content