This page takes you from the purchase to a paired phone sending its first SMS. It assumes no prior knowledge of the product. Allow about a quarter of an hour, half of it spent downloading.
The installation fits in one command. A script installs Docker if it is missing, downloads the
files of the latest version, checks that they have not been altered, writes the configuration,
starts the gateway and installs an operations command, sms-gateway. You have nothing to compile
and no server address to type in.
What you are installing
The gateway is one program, which listens on one port and keeps everything in one data folder (a Docker volume). It runs in a Docker container, on your machine.
| You do not need | Why |
|---|---|
| A database server | The database is a single file inside the data folder, created at the first start |
| A separate web server for the dashboard | The dashboard is built into the gateway and served on the same port as the API |
| Any third-party service | The gateway contacts nobody except our license server and the webhooks you configure yourself. Your SMS, contacts and logs never leave your machine |
Practical consequence: backing up the data folder backs up the whole installation, and moving it moves the gateway (see Backup and restore and Migrating to another server).
What you need
| Item | Detail |
|---|---|
| A machine | Linux amd64 or arm64 (VPS, free Oracle Cloud instance, server or mini-PC on your premises, 64-bit Raspberry Pi), or a Windows or macOS computer with Docker Desktop |
| Administrator access | root or sudo on Linux, PowerShell opened as administrator on Windows |
| Outbound internet access | To download Docker and the gateway, and to activate the license. See Network access |
| For domain mode | A domain name whose A (or AAAA) record points at the machine, and ports 80 and 443 reachable from the internet |
| For local network mode | Phones connected to the same network as the machine |
| An Android phone | Android 8 or later, with an active SIM and an SMS plan, kept on charge |
| Your license key | Shown in your customer area after purchase |
Nothing is compiled on your machine: the gateway is published ready to run for amd64 and arm64
processors, and Docker picks the right one. A small machine is enough: the gateway itself uses very
little memory, and the smallest plan of most hosting providers is sufficient to start.
The path in five steps
Step 1: create your account and buy
Go to sms-gateway.araylab.com. The Buy button first asks you to sign in: enter your email address and you receive a sign-in link. There is no password to remember. You land in your customer area, empty for now.
Payment is handled by Gumroad, which also issues the invoice and handles VAT. In Madagascar, payment by Mobile Money (MVola, Orange Money, Airtel Money) is validated by hand: the license appears after that validation, not instantly.
Once the payment is confirmed, you receive an email announcing that the license is ready. For your security, that email does not contain the key: it sends you back to your customer area.
Step 2: copy your license key
In the customer area, the license shows its key, in the form XXXX-XXXX-XXXX-XXXX, with a button to
copy it. The installation instructions and the download link for the Android app appear there too,
as soon as the license is active.
You can come back at any time to display the key again, and regenerate it if it has leaked (see Regenerate the key).
Step 3: run the installation command
On the machine that will host the gateway, in a terminal.
Linux (VPS, Oracle Cloud, local server, Raspberry Pi):
curl -fsSL https://sms-gateway.araylab.com/install.sh | sudo bash
Without options, the script asks you two questions: whether it may install Docker if it is missing, and whether your phones will reach the gateway through a domain name or on the local network (see Choose the mode). To answer everything up front:
# With a domain name: automatic HTTPS (recommended)
curl -fsSL https://sms-gateway.araylab.com/install.sh | sudo bash -s -- --domain sms.example.com --yes
# On the local network, without a domain name
curl -fsSL https://sms-gateway.araylab.com/install.sh | sudo bash -s -- --lan --yes
macOS: the same command, with Docker Desktop installed and started beforehand. The installation
goes into ~/sms-gateway, and the sms-gateway command is placed in that folder.
Windows (Docker Desktop required), in PowerShell opened as administrator:
irm https://sms-gateway.araylab.com/install.ps1 | iex
On Windows, the script asks no question about the mode: it installs in local network mode by default. To change that, set these variables in the same PowerShell window before running the command:
| Variable | Default | Effect |
|---|---|---|
$env:SMSGW_DOMAIN |
empty: local network mode | Installs in domain mode, with automatic HTTPS on that name. On an existing installation, switches it to domain mode |
$env:SMSGW_PORT |
8080 |
Port of the gateway. Taken into account on the first installation only |
$env:SMSGW_VERSION |
the latest published version | Installs that precise version X.Y.Z |
$env:SMSGW_DOMAIN = "sms.example.com"
irm https://sms-gateway.araylab.com/install.ps1 | iex
If Docker Desktop is missing, the Windows script offers to install it with winget, then stops and
asks you to restart Windows, start Docker Desktop once, and run the command again. It installs into
%ProgramData%\SmsGateway, opens port 8080 in the Windows firewall for private networks in
local network mode, then opens the dashboard in your browser.
At the end, every script shows the dashboard address and the setup code used to create
the administrator account (step 4). On Linux and macOS, sms-gateway url shows the address again
and sms-gateway setup-code shows the code, as long as no account exists.
Prefer to read the script first? Piping a downloaded script into
bashmeans trusting it. You can download it, read it, then run it:curl -fsSL https://sms-gateway.araylab.com/install.sh -o install.sh less install.sh sudo bash install.sh --domain sms.example.comThe script, for its part, checks everything it downloads next against a published SHA-256 checksum: a file that does not match stops the installation before anything is installed.
What the script does, in order
- It checks that it runs as
rooton a Linuxamd64orarm64machine (or on macOS). - It installs Docker if it is missing, through Docker's official script, after asking you (or
without asking with
--yes). - It finds the latest published version (or the one given with
--version), downloads its files and checks their checksum. - It asks for the mode if no option set it. In domain mode, it checks that the domain points at this machine and that ports 80 and 443 are free. A problem gives a warning and a question, not a hard stop: you can fix DNS afterwards.
- In local network mode, it installs
avahi-daemon, which makes the name<machine-name>.localresolvable on your network. - It writes the configuration to
/opt/sms-gateway/.env, readable byrootonly. An existing configuration is kept: running the command again updates the gateway without erasing your settings. - It downloads the gateway, starts it and waits until it answers (at most two minutes).
- It installs the
sms-gatewaycommand and shows the dashboard address, the setup code and the next steps.
The options (Linux and macOS)
| Option | Default | Effect |
|---|---|---|
--domain <name> |
none: the script asks | Domain mode: HTTPS on that name, certificate obtained automatically. Ports 80 and 443 must be reachable. Sets the public address to https://<name> |
--lan |
none: the script asks | Local network mode: the gateway is served over HTTP on http://<machine-name>.local:<port>, to every device of the local network |
--port <n> |
8080 |
Port of the gateway on the machine. In local network mode, it is part of the address written into the QR codes |
--dir <path> |
/opt/sms-gateway (~/sms-gateway on macOS) |
Installation folder, where the configuration lives. System folders are refused |
--version <X.Y.Z> |
the latest published version | Installs that precise version, for example to stay on a version you have tested |
--yes |
off | Asks no question: accepts the Docker installation and continues past warnings (DNS, busy ports) |
--no-docker-install |
off | Never installs Docker. If it is missing, the script stops and says so |
--help |
- | Shows the help and does nothing else |
How the options interact:
- Without
--domainor--lan, the script asks. With--yesand no mode on a first installation, it stops: there is no default mode, because the wrong one gives phones that never connect. - On a new run over an existing installation, the mode and the port already recorded are kept.
Giving
--domainor--lanagain switches the mode: the public address changes, and phones already paired must be paired again. --portis only taken into account on the first installation. Afterwards, the port recorded in the configuration wins; changeGATEWAY_PORTin the.envfile instead (see Settings you can change).--yesalso answers yes to warnings. In domain mode, an installation with a wrong DNS or with ports 80/443 taken continues anyway: the gateway works, but HTTPS will not until you fix the cause.
Step 4: open the dashboard, create the administrator, paste the key
Open the address shown by the script. A fresh installation opens the Create the first account screen: Setup token, name, email address, password. The password must be at least 12 characters long. This first account is the superadministrator, and the screen that creates it closes for good as soon as an account exists.
The setup token is the setup code shown at the end of the script. It proves that the person creating the account has access to the machine: without it, the first stranger to find a freshly installed gateway on the internet could make themselves its administrator. If you no longer have it in front of you:
| Where | Command |
|---|---|
| Linux, macOS | sms-gateway setup-code |
| Windows | docker compose exec gateway /usr/local/bin/gateway setup-token, from %ProgramData%\SmsGateway |
The code disappears as soon as the account is created: the commands above then answer that there is none left, and that is expected.
Creating the account does not sign you in: the screen confirms the creation, then sends you to the sign-in screen, where you type the email address and password you just chose.
Once signed in, an Activate this gateway window appears. Paste the key copied in step 2. Spaces, dashes and lowercase letters are accepted. There is nothing else to type: the gateway already knows how to reach our license server.
If our server does not answer at that moment, the window says Our server is not answering. Your key is recorded anyway and nothing is suspended: the gateway sends normally and retries the activation on its own. An outage on our side is never treated as an invalid license. Details in License.
Step 5: install the Android app and pair the phone
- Download the app from your customer area, on the phone itself or on a computer and then copied over. The app is not on the Play Store: the link is given only to license holders.
- Install it. Android asks you to allow installation from the source you used (browser or file manager). Allow it for this installation.
- In the dashboard, open Devices and click Pair a device. A QR code appears, with a fallback code for a phone whose camera does not work. It is valid for five minutes and can be used only once; Generate a new code gives you another.
- In the app, scan the QR code. The phone connects to the gateway and appears online in Devices, with its SIM cards.
- Send a test SMS from Quick send, then reply to it from another phone: the reply shows up in Conversations.
The app asks to be exempted from battery optimization and shows a permanent notification: that is what keeps it running in the background. Keep the phone on charge. Everything about phones (SIM, daily quota, keeping the phone awake) is in Devices.
If you type the address by hand instead of scanning, type it in full, with
https://orhttp://(https://sms.example.comorhttp://server.local:8080). Without it, the app assumes an HTTPS connection, which fails on a local network installation.
Choose the mode: domain or local network
This is the only real choice in the installation, and it decides the address the phones dial. That address is written into every pairing QR code, then kept by the phone. A wrong address gives a QR code that scans perfectly and a phone that never reaches anything.
| Question | Domain mode (--domain) |
Local network mode (--lan) |
|---|---|---|
| For whom | VPS, Oracle Cloud, any server reachable from the internet | A server or computer on your premises, not exposed |
| Dashboard address | https://sms.example.com |
http://<machine-name>.local:8080 |
| Encryption | HTTPS, certificate obtained and renewed automatically (Let's Encrypt) | None: plain HTTP on your local network |
| Where phones can be | Anywhere, 4G included | Only on the same network as the machine |
| What the machine exposes | Ports 80 and 443. The gateway itself stays private | Port 8080, to every device on the local network |
| Prerequisites | DNS pointing at the machine, ports 80 and 443 open | The .local name must resolve on your network (the script installs what is needed on Linux) |
Choose domain mode whenever you can: it has none of the caveats below.
What local network mode implies
- The Android app accepts unencrypted connections only to a
.localname. A plain IP address such ashttp://192.168.1.20:8080is refused by the phone. That is why the installer useshttp://<machine-name>.local:8080. - Check that the phone resolves that name before going further: open
http://<machine-name>.local:8080/healthin the phone's browser, connected to the same wifi. A{"status":"ok",...}response means pairing can work. - The dashboard session travels unencrypted on your network. The installer sets
GATEWAY_INSECURE_COOKIES=true, otherwise browsers would refuse to keep you signed in over plain HTTP. Acceptable on a company network you control, not on a shared wifi. - The machine's name must not change, since it is written into the QR codes. Renaming it forces you to pair the phones again.
- A guest wifi often isolates its devices: the phone sees the internet but not the machine. The
/healthtest above reveals it. Put the phones on the internal network.
Everything specific to this path (fixed address, guest wifi, access from outside) is in Local server.
What domain mode implies
- The DNS must point at the machine before the certificate can be obtained. The script warns you if it does not; the certificate is obtained automatically as soon as the DNS is right.
- Ports 80 and 443 must be reachable from the internet, port 80 included: it is used to prove you own the domain. Open them in the machine's firewall and at your hosting provider, which often filters too.
- Nothing else is exposed: a small web server (Caddy) handles HTTPS in front of the gateway, which itself only listens on the machine.
- If a proxy such as Cloudflare sits in front of the domain, disable it while the certificate is obtained.
The sms-gateway command
Installed on Linux and macOS, it is run as root (or with sudo) from any folder.
| Command | What it does |
|---|---|
sms-gateway status |
State of the gateway (and of the HTTPS server in domain mode) |
sms-gateway logs |
Gateway logs, continuously. Ctrl+C to exit |
sms-gateway url |
Shows the dashboard address |
sms-gateway setup-code |
Shows the setup code, as long as no account exists |
sms-gateway update [X.Y.Z] |
Moves to the latest version, or to the one given. Data and configuration are kept |
sms-gateway restart |
Restarts the gateway, and applies a change made to the .env file |
sms-gateway stop / start |
Stops or starts the gateway again without deleting anything |
sms-gateway backup |
Writes an archive of the whole data folder to the current folder (see Backup) |
sms-gateway uninstall |
Removes the gateway and the command. The data is kept |
sms-gateway uninstall --purge |
Also deletes the data: everything is lost, messages, contacts, accounts and master key |
Running the installation command again has the same effect as sms-gateway update.
On Windows, there is no sms-gateway command. Open PowerShell in %ProgramData%\SmsGateway and
use Docker Compose directly:
| To | Command |
|---|---|
| See the state | docker compose ps |
| Read the logs | docker compose logs -f gateway |
| Restart | docker compose restart gateway |
| Update | Run the installation command again |
| Read the setup code | docker compose exec gateway /usr/local/bin/gateway setup-token |
Keep the computer on and Docker Desktop set to start with Windows: the gateway runs only while Docker Desktop is running.
Stopping or restarting loses nothing. While the gateway is down, phones keep outgoing and incoming messages in their own queue and hand them over when it comes back.
Regenerate the key
In the customer area, Regenerate key replaces your license key with a new one. Do it if the key has leaked (a support ticket, a screenshot, a former contractor), or to free the license before installing on another machine.
| What changes | What does not change |
|---|---|
| The old key is refused immediately for activating or renewing | Your license: tier, version line, purchase date |
| No machine is attached to the license any more | The gateway already installed keeps working and sending |
The installed gateway does not stop. However, it can no longer renew its activation as long as it carries the old key: open License in the dashboard and enter the new key there. Two regenerations must be at least a minute apart.
Troubleshooting
Always start with sms-gateway status, then sms-gateway logs.
| Symptom | Likely cause and fix |
|---|---|
| Warning: ports 80 or 443 already in use | Another web server (Apache, Nginx, a hosting panel) holds them. sudo ss -ltnp 'sport = :80' names it. Stop it, use another machine, or see VPS, existing web server. Continuing anyway leaves you without HTTPS |
| Startup fails because port 8080 is in use | Set GATEWAY_PORT=8090 (or another free port) in /opt/sms-gateway/.env, then run the installation command again. In local network mode, add --lan so that the address in the QR codes takes the new port |
| Warning: the domain does not point at this machine | Fix the A record at your registrar, wait for propagation (dig +short sms.example.com), then sms-gateway restart |
| The browser reports an invalid certificate | The certificate has not been obtained yet: DNS not propagated, or port 80 closed at the host. sms-gateway logs caddy says why |
| The script says Docker is missing and stops | You used --no-docker-install, or declined the installation. Install Docker with its compose plugin, or run again without that option |
| Windows: Docker Desktop does not start | Docker Desktop needs virtualization enabled in the BIOS and WSL 2. Start it once by hand, then run the command again |
| The script waits, then says the gateway does not answer | The last log lines are shown. See When the gateway refuses to start |
| The dashboard shows but signing in fails with no message | You are using plain HTTP on an address other than the one the installer set up. Use the address shown by sms-gateway url |
| The setup token is refused | It was mistyped, or it belongs to another installation. Read it again with sms-gateway setup-code and paste it as is |
sms-gateway setup-code answers that there is no token |
An account already exists. Sign in with it. If nobody on your side created it, reinstall from scratch with sms-gateway uninstall --purge then the installation command |
| The phone does not pair: nothing happens after the scan | The phone cannot reach the address in the QR code. Open https://sms.example.com/health (or http://<name>.local:8080/health) in the phone's browser. If it fails: guest wifi, phone on another network, .local name not resolved, ports closed |
| The phone refuses the address | An http:// address that is not a .local name (an IP for example). Use the .local name, or switch to domain mode |
| The QR code is refused as expired | A QR code lives five minutes and works once. Click Generate a new code |
| The QR code carries a wrong address | Run the installation command again with the right --domain or --lan, then generate a new QR code. Codes already issued keep the old address |
| The license stays on Local token due for renewal | The machine cannot reach our license server (outbound firewall, company proxy), or the key has been regenerated since. Sending is not affected. See License |
Network access
The machine needs outbound HTTPS access to:
| Address | What for | When |
|---|---|---|
get.docker.com |
Installing Docker | Only if Docker is missing |
ghcr.io |
Downloading the gateway | Installation and updates |
bck2.araylab.com |
Downloading the installation files of a version | Installation and updates |
bck1.araylab.com |
License activation and periodic renewal | While the gateway runs |
api.ipify.org |
Comparing the machine's public address with the DNS | Installation in domain mode |
If bck1.araylab.com is blocked, nothing stops working, but the license screen will keep saying that
our server does not answer.
Settings you can change
The installer writes the configuration to /opt/sms-gateway/.env (readable by root only; on
Windows, %ProgramData%\SmsGateway\.env). For a standard installation, you have nothing to
add. Everything you tune day to day (sending window, retries, time zone, notifications, routing
rules...) is set in the dashboard, under Settings and the related screens, not here: see
Settings. No setting of the dashboard has an equivalent variable, and no
variable below can be changed from the dashboard.
After editing the .env file, apply the change with sms-gateway restart (on Windows:
docker compose up -d in the installation folder). An update or a new run of the installer keeps
the file; it only rewrites the version and, if you give --domain or --lan again, the mode lines.
In the .env file
These lines are written by the installer. You may edit them, knowing what each one drives.
| Variable | Default (written by the installer) | Effect |
|---|---|---|
SMSGW_MODE |
domain or lan, per your choice |
Remembers the mode, so that an update keeps it. Read by the installer only |
GATEWAY_PUBLIC_URL |
https://<domain> or http://<machine>.local:<port> |
The address phones and browsers use. Written into every pairing QR code (https:// gives an encrypted phone link, http:// an unencrypted one) and used as the base of short links. Read at startup only |
GATEWAY_DOMAIN |
your domain, or localhost in local mode |
The name the HTTPS certificate is requested for. Must be the host of GATEWAY_PUBLIC_URL |
COMPOSE_PROFILES |
tls in domain mode, empty in local mode |
tls starts the HTTPS server (Caddy) in front of the gateway, on ports 80 and 443. Empty: no HTTPS server |
GATEWAY_PORT |
8080 (or --port) |
Port of the gateway on the machine. In local mode, it must match the port in GATEWAY_PUBLIC_URL |
GATEWAY_BIND |
127.0.0.1 in domain mode, 0.0.0.0 in local mode |
Which network interface the port listens on. 127.0.0.1: only the machine itself (the HTTPS server relays the rest). 0.0.0.0: every device of the network, in plain HTTP |
GATEWAY_INSECURE_COOKIES |
false in domain mode, true in local mode |
true lets browsers keep you signed in over plain HTTP, at the price of a session that travels unencrypted. Changes nothing else |
GATEWAY_VERSION |
the installed version | The version that starts. Prefer sms-gateway update X.Y.Z, which also downloads the matching files |
GATEWAY_IMAGE |
ghcr.io/andritianaa/sms-gateway |
Where the gateway is downloaded from. Do not change |
The file also lists, commented out, the settings below (an update adds that list once to a .env
written by an earlier version). Remove the # in front of a line to use it, then restart. The
provided docker-compose.yml passes each of them to the gateway; left out or empty, the gateway uses
its default. Because they live in .env and not in docker-compose.yml, an update keeps them.
| Variable | Default | Effect |
|---|---|---|
GATEWAY_LOG_LEVEL |
info |
Detail of the technical logs (sms-gateway logs and the technical view of Activity log): debug, info, warn or error. debug is verbose; no level ever writes the text of an SMS or a full phone number |
GATEWAY_TRUSTED_PROXIES |
172.31.254.2/32, the address of the built-in HTTPS server |
The reverse proxies allowed to report the visitor's real IP address, comma separated (addresses or CIDR blocks). The gateway uses that IP for sign-in lockouts and rate limits. Change it only if you put your own proxy in front: see VPS, existing web server |
GATEWAY_LOG_RETENTION_DAYS |
30 |
How many days an entry of the Activity log is kept before being deleted, from 1 to 3650. A longer retention makes the database grow; entries older than the new value are deleted by the next cleanup. See Activity log |
GATEWAY_UPTIME_RETENTION_DAYS |
90 |
How many days the connection history of the phones is kept for the Device uptime charts, from 90 to 3650. Below 90 days the 90-day chart would lose its oldest bars, hence the floor. Older history is deleted by the next hourly cleanup. See Devices |
GATEWAY_ROUTING_MODE |
round_robin |
How messages are spread across SIM cards when no routing rule picks one: round_robin (in turn), quota (the SIM with the most room left in its daily quota) or carrier (the SIM of the recipient's carrier). See Routing |
GATEWAY_CARRIER_PREFIXES |
empty | Which number prefixes belong to which carrier, in the form Telma:+26134,+26138;Orange:+26132. The carrier names must be written as the SIM cards report them in Devices. Required in carrier mode, ignored otherwise. A number that matches no prefix is sent as in quota mode |
GATEWAY_WEBHOOKS_ALLOW_PRIVATE_NETWORKS |
true |
Allows webhooks to addresses of your private network (a CRM on the same network, for example). false restricts them to public addresses. The machine's own addresses and cloud metadata addresses are refused in every case |
Interactions to know:
- The mode is four lines that go together:
GATEWAY_PUBLIC_URL,GATEWAY_DOMAIN,COMPOSE_PROFILESandGATEWAY_BIND(plusGATEWAY_INSECURE_COOKIES). Changing one without the others gives, for example, a working HTTPS site and phones that do not pair. To change mode or domain, run the installer again with--domainor--lan: it rewrites them together. - Changing
GATEWAY_PUBLIC_URLdoes not update phones already paired: they keep the address of their QR code. Pair them again after such a change. GATEWAY_TRUSTED_PROXIESset too wide (for example0.0.0.0/0) lets anyone pretend to be any IP address and get around the sign-in lockout. Left empty, the gateway sees every visitor as the proxy's address, and they all share the same limits.
Advanced settings
The gateway reads the two variables below too. They are not listed in the file the installer writes, because a customer installation rarely needs them.
| Variable | Default | Effect |
|---|---|---|
GATEWAY_SECRET_KEY |
generated at the first start and kept in secret.key |
The master key, 32 bytes in base64, if you prefer to keep it in a secret manager. Passed from .env by the provided docker-compose.yml. When set, it replaces secret.key. A different key from the one the installation started with makes paired phones, webhook secrets and the stored license key unreadable. See The master key |
GATEWAY_ENV_FILE |
gateway.env next to the program |
A key=value file read before everything else. A variable already set in the environment always wins over the file. Missing file: ignored. Unreadable file: startup is refused. Not passed by the provided docker-compose.yml, whose .env plays that role |
Leave these alone. They exist in the program, but a customer installation has no use for them:
| Variable | Default | Why you should not set it |
|---|---|---|
GATEWAY_DEV |
false |
Development mode. It removes the protection of the session cookie, accepts an unencrypted public address on any host, and lets the license server address and keys be replaced. The provided docker-compose.yml does not pass it, on purpose |
GATEWAY_LICENSE_SERVER_URL |
our license server, built in | Ignored unless GATEWAY_DEV=true (a warning says so in the logs). Must be https:// |
GATEWAY_LICENSE_PUBLIC_KEYS |
our public keys, built in | Same rule: ignored unless GATEWAY_DEV=true |
GATEWAY_ADDR |
:8080 |
Listening port inside the container. Change GATEWAY_PORT instead; changing this one breaks the health check |
GATEWAY_DATA_DIR |
/data |
Data folder inside the container. Changing it moves the data out of the volume and changes the machine fingerprint |
Backups have no variable: the automatic backup is set in the dashboard, under Settings > Backup and export. See Backup.
Two rules apply to every variable:
- An empty value counts as absent: the default applies.
- An invalid value stops the gateway at startup, with a message naming the variable, rather than running on a value you did not choose. See the next section.
When the gateway refuses to start
The message is in sms-gateway logs, on a line containing gateway stopped. The gateway restarts
in a loop until the cause is fixed.
| Cause | What the log says |
|---|---|
| Unknown log level | GATEWAY_LOG_LEVEL: "..." is not one of debug, info, warn, error |
| Activity log retention out of range | GATEWAY_LOG_RETENTION_DAYS: "..." is not a number of days between 1 and 3650 |
| Uptime history retention out of range | GATEWAY_UPTIME_RETENTION_DAYS: "..." is not a number of days between 90 and 3650 |
| Unknown routing mode | GATEWAY_ROUTING_MODE: "..." is not one of round_robin, quota, carrier |
carrier mode without a prefix table |
GATEWAY_ROUTING_MODE=carrier requires GATEWAY_CARRIER_PREFIXES |
| Malformed prefix table | ... is not in the form Carrier:+prefix,+prefix |
| A yes/no setting with another value | <VARIABLE>: "..." is not a boolean (use true or false) |
Plain http:// public address that is not a .local name nor localhost |
GATEWAY_PUBLIC_URL: "..." is plain http on a non-local host; ... |
| Unreadable proxy address | GATEWAY_TRUSTED_PROXIES: "..." is neither a CIDR block nor an address |
| Master key of the wrong size | key is N bytes, expected 32 or key is not valid base64 |
| Data folder unusable | create data dir: ..., or an error opening the database |
GATEWAY_ENV_FILE (or gateway.env) present but unreadable |
read <path>: ... |
License server address in plain http:// |
GATEWAY_LICENSE_SERVER_URL: "..." must use https ... |
Why a plain http:// address is refused: over HTTP, the dashboard session would travel
unencrypted across the internet. Only a .local name (local network mode) or localhost is
accepted without HTTPS.
Why carrier mode is refused without a table: it would route nothing by carrier, and you would
believe you pay the on-network rate while paying the cross-network one.
A restore that fails (see Backup and restore) never prevents startup: the gateway starts on the database it has, and logs the error.
The master key
At the first start, the gateway creates a master key, secret.key, in its data folder. It protects
the secrets of the paired phones, the webhook secrets and your license key stored in the database,
and it identifies your machine to our license server (only a fingerprint derived from it is sent,
never the key).
Losing it means pairing every phone again and entering the license key again. It lives next to
the database, not inside it: a database export alone does not contain it, whereas the
sms-gateway backup archive, which copies the whole data folder, does.
What the data folder contains
| File | Role |
|---|---|
gateway.db (and -wal, -shm) |
The database: messages, contacts, conversations, settings, accounts |
secret.key |
The master key |
setup.token |
The setup code. Present only as long as no account exists |
backups/ |
The backups made from the dashboard or the API |
restore.pending.* |
Present only while a restore waits for the next restart |
gateway.db.pre-restore-... |
The previous database, set aside after a restore, kept until you delete it |
The container is locked down: it cannot write anywhere except this folder and a temporary space used
for database exports. If you replace the provided docker-compose.yml with your own setup, keep the
hostname: sms-gateway line (without it, every recreation of the container looks like a move to our
license server) and the /tmp mount (without it, the database export fails).
Backup
The whole installation lives in the data folder. The simplest backup is:
sudo sms-gateway backup
Run from the folder where you want the archive, it stops the gateway for the few seconds of the copy (phones keep their messages meanwhile), writes
sms-gateway-backup-<date>.tar.gz to the current folder, starts it again and waits until it answers. That archive contains
the master key along with the database: store it as the most sensitive document of the
installation, and not on the same machine.
The gateway also backs itself up automatically, without interrupting sending: every day at 03:00
in the platform time zone by default, seven copies kept, in the data folder. Change the time, the
frequency and the number of copies, or turn it off, in Settings > Backup and export, which also
lists and restores backups and exports your data as CSV or JSON. These copies stay on the machine:
download one, or copy a sms-gateway backup archive, to keep a copy elsewhere.
Everything is detailed in Backup and restore.
The hosting guides
This page covers the installation itself. What is specific to each kind of machine has its own guide:
| Where | What it adds |
|---|---|
| Oracle Cloud Always Free | Create the free instance and open the ports at both levels (Oracle and the instance) |
| VPS | Prepare a rented server: DNS, firewall, existing web server, automatic startup |
| Local server | The .local name, guest wifi, access from outside and availability |
Next
| You want to | Go to |
|---|---|
| Manage your phones and SIM cards | Devices |
| Send your first messages | Sending |
| Send through the API | API |
| Back up and restore | Backup and restore |
| Move to another machine | Migrating to another server |
| Know what protects your data | Security |