SMSTools3 is an SMS gateway daemon that communicates with GSM modems to send and receive text messages. When combined with LXC containers and infrastructure-as-code tools like Terraform, you can create isolated, reproducible SMS processing environments. This guide covers installing and configuring SMSTools in an LXC container, both manually and using automation tools.
This guide uses older Huawei GSM modems that are widely available and inexpensive. Common models include:
These modems are legacy devices from the 3G era, making them perfect for SMS applications. Since they’re no longer current technology, you can find them used on marketplaces like eBay or Alibaba typically ranging from $5-$15 USD depending on condition and seller. Some listings offer bulk quantities at even better prices.
Why these old modems work great:
/dev/ttyUSB0)The 4G LTE Mall provides detailed specifications for these models if you need more technical information.
LXC containers provide an ideal environment for GSM modem management:
For SMS processing, this means you can attach a GSM modem via USB passthrough and have it appear as a standard /dev/ttyUSB0 device inside the container, exactly as it would on a bare metal system.
On your Proxmox host, create an Ubuntu container:
pct create 751 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst \
--hostname gsm-modem-1 \
--memory 512 \
--cores 1 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--storage local-lvm \
--rootfs local-lvm:4
Find your modem’s USB device IDs:
lsusb | grep Huawei
# Output: Bus 001 Device 005: ID 12d1:1c10 Huawei Technologies Co., Ltd.
Edit the container configuration (/etc/pve/lxc/751.conf) and add:
lxc.cgroup2.devices.allow: c 188:* rwm
lxc.mount.entry: /dev/bus/usb/001 dev/bus/usb/001 none bind,optional,create=dir
Start the container:
pct start 751
Enter the container and install required packages:
pct enter 751
# Update package list
apt update
# Install SMSTools3
apt install -y smstools
Create the configuration file at /etc/smsd.conf:
devices = GSM1
outgoing = /var/spool/sms/outgoing
checked = /var/spool/sms/checked
incoming = /var/spool/sms/incoming
logfile = /var/log/smstools/smsd.log
infofile = /var/run/smstools/smsd.working
pidfile = /var/run/smstools/smsd.pid
failed = /var/spool/sms/failed
sent = /var/spool/sms/sent
stats = /var/log/smstools/smsd_stats
loglevel = 5
[GSM1]
device = /dev/ttyUSB0
check_memory_method = 31
incoming = yes
baudrate = 115200
SMSTools expects specific directories for message queue management:
mkdir -p /var/spool/sms/{incoming,outgoing,checked,failed,sent}
mkdir -p /var/log/smstools
mkdir -p /var/run/smstools
Enable and start SMSTools:
systemctl enable smstools
systemctl start smstools
Verify it’s running:
systemctl status smstools
Check the log file for successful modem initialization:
tail -f /var/log/smstools/smsd.log
You should see output indicating the modem was detected and is ready to receive messages.
For reproducible deployments across multiple containers, Ansible provides declarative configuration management. Here’s the key tasks from a production playbook:
---
- name: Deploy SMSTools on LXC Container
hosts: gsm-modem-1
become: yes
tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
- name: Install SMSTools and dependencies
ansible.builtin.apt:
name:
- smstools
- curl
- wget
state: present
- name: Create SMSTools directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0755"
loop:
- /var/spool/sms
- /var/spool/sms/incoming
- /var/spool/sms/outgoing
- /var/spool/sms/checked
- /var/spool/sms/failed
- /var/spool/sms/sent
- /var/log/smstools
- /var/run/smstools
- name: Deploy SMSTools configuration
ansible.builtin.copy:
src: smsd.conf
dest: /etc/smsd.conf
mode: '0644'
notify: restart smstools
- name: Enable and start SMSTools service
ansible.builtin.systemd:
name: smstools
state: started
enabled: yes
handlers:
- name: restart smstools
ansible.builtin.systemd:
name: smstools
state: restarted
Run the playbook:
ansible-playbook -i inventory.yml deploy_smstools.yml
[GSM1]/dev/ttyUSB0 for first USB modem)31 works with most Huawei modems.Check if the modem responds to AT commands:
apt install minicom
minicom -D /dev/ttyUSB0
# In minicom, type:
AT
# Should respond: OK
AT+CPIN?
# Should respond: +CPIN: READY (if SIM has no PIN) or +CPIN: SIM PIN (if PIN required)
Exit minicom with Ctrl+A then X.
Send a test SMS to your modem’s phone number, then check:
ls -la /var/spool/sms/incoming/
cat /var/spool/sms/incoming/GSM1.*
Incoming messages are written as individual files with metadata:
From: +1234567890
Received: 25-12-30 11:45:32
Subject: Test message
Message body here
If /dev/ttyUSB0 doesn’t appear:
# Check if USB device is visible
lsusb
# Check kernel messages
dmesg | grep ttyUSB
# Verify udev rules
udevadm info -a -n /dev/ttyUSB0
Check the log for errors:
journalctl -u smstools -n 50
tail -100 /var/log/smstools/smsd.log
Common issues:
smsd, ensure it can access /dev/ttyUSB0Verify the modem memory method:
# Try different check_memory_method values
# Edit /etc/smsd.conf and change to: 1, 2, or 31
systemctl restart smstools
SMSTools simply writes incoming messages to the filesystem. To process them automatically, you can:
/var/spool/sms/incoming with tools like inotifyFor a complete SMS gateway implementation using Go, see the Mailer-Go project which demonstrates:
fsnotify package