Deploy iCommerce on Fly.io
Deploy StateSet iCommerce on Fly.io with automatic HTTPS, persistent volumes, and global edge deployment. This guide walks you through deploying a production-ready iCommerce Gateway that scales automatically.Goal
Deploy a StateSet iCommerce Gateway on Fly.io with:- Persistent storage for configuration and workspace data
- Automatic HTTPS with custom domain support
- Discord, Telegram, and other channel integrations
- Global edge deployment for low latency
What you’ll build
Prerequisites
Before you begin, ensure you have:- flyctl CLI installed
- Fly.io account (free tier works)
- StateSet API credentials
- Model provider credentials (Anthropic, OpenAI, etc.)
- Discord bot token
- Telegram bot token
- WhatsApp Business API credentials
Quick path (experienced operators)
If you’re familiar with Fly.io, follow this condensed workflow:- Clone repo and customize
fly.toml - Create app and volume
- Set secrets via
fly secrets set - Deploy with
fly deploy - SSH in to create config or use Control UI
1) Create the Fly app
Clone the repository and create a new Fly app:2) Configure fly.toml
Create or editfly.toml to match your app name and requirements:
Key settings explained
| Setting | Purpose |
|---|---|
--bind lan | Binds to 0.0.0.0 so Fly’s proxy can reach the gateway |
--allow-unconfigured | Starts without a config file (create one after deploy) |
internal_port = 3000 | Must match --port 3000 for Fly health checks |
memory = "2048mb" | 512MB is too small; 2GB recommended |
STATESET_STATE_DIR = "/data" | Persists state on the volume |
3) Set secrets
Configure your API keys and tokens as Fly secrets:Non-loopback binds (
--bind lan) require STATESET_GATEWAY_TOKEN for security. Treat these tokens like passwords.4) Deploy
Deploy the application:5) Create configuration file
SSH into the machine to create a proper configuration:With
STATESET_STATE_DIR=/data, the config path is /data/stateset.json.6) Access the Gateway
Control UI
Open in your browser:https://my-icommerce.fly.dev/
Enter your gateway token (from STATESET_GATEWAY_TOKEN) to authenticate.
Logs
SSH Console
Troubleshooting
”App is not listening on expected address”
The gateway is binding to127.0.0.1 instead of 0.0.0.0.
Fix: Add --bind lan to your process command in fly.toml.
Health checks failing / connection refused
Fly can’t reach the gateway on the configured port. Fix: Ensureinternal_port matches the gateway port (set --port 3000 or STATESET_GATEWAY_PORT=3000).
OOM / Memory issues
Container keeps restarting or getting killed. Signs:SIGABRT, memory allocation errors, or silent restarts.
Fix: Increase memory in fly.toml:
Gateway lock issues
Gateway refuses to start with “already running” errors. This happens when the container restarts but the PID lock file persists on the volume. Fix: Delete the lock file:Config not being read
If using--allow-unconfigured, the gateway creates a minimal config. Your custom config at /data/stateset.json should be read on restart.
Verify the config exists:
Writing config via SSH
Thefly ssh console -C command doesn’t support shell redirection. To write a config file:
fly sftp may fail if the file already exists. Delete first with fly ssh console --command "rm /data/stateset.json".State not persisting
If you lose credentials or sessions after a restart, the state directory is writing to the container filesystem. Fix: EnsureSTATESET_STATE_DIR=/data is set in fly.toml and redeploy.
Updates
Updating machine command
If you need to change the startup command without a full redeploy:After
fly deploy, the machine command may reset to what’s in fly.toml. If you made manual changes, re-apply them after deploy.Private deployment (hardened)
By default, Fly allocates public IPs, making your gateway accessible athttps://your-app.fly.dev. This is convenient but means your deployment is discoverable by internet scanners.
When to use private deployment
- You only make outbound calls/messages (no inbound webhooks)
- You use ngrok or Tailscale tunnels for webhook callbacks
- You access the gateway via SSH, proxy, or WireGuard
- You want the deployment hidden from internet scanners
Setup
Usefly.private.toml instead of the standard config:
fly ips list should show only a private type IP:
Accessing a private deployment
- Local proxy (simplest)
- WireGuard VPN
- SSH only
Webhooks with private deployment
If you need webhook callbacks (Twilio, Telnyx, etc.) without public exposure:- ngrok tunnel - Run ngrok inside the container or as a sidecar
- Tailscale Funnel - Expose specific paths via Tailscale
- Outbound-only - Some providers work fine for outbound calls without webhooks
Security comparison
| Aspect | Public | Private |
|---|---|---|
| Internet scanners | Discoverable | Hidden |
| Direct attacks | Possible | Blocked |
| Control UI access | Browser | Proxy/VPN |
| Webhook delivery | Direct | Via tunnel |
Cost
With the recommended configuration (shared-cpu-2x, 2GB RAM):| Component | Cost |
|---|---|
| Compute | ~$10-12/month |
| Storage (1GB) | ~$0.15/month |
| Bandwidth | Varies by usage |
| Total | ~$10-15/month |
Fly.io’s free tier includes some allowance. See Fly.io pricing for details.
Notes
- Fly.io uses x86 architecture (not ARM)
- The Dockerfile is compatible with both architectures
- For WhatsApp/Telegram onboarding, use
fly ssh console - Persistent data lives on the volume at
/data