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
Next steps
Messaging Channels
Set up WhatsApp, Telegram, and other messaging integrations.
Skills
Configure and extend agent capabilities with custom skills.
CLI Reference
Learn the full CLI command set for managing your iCommerce instance.
Security
Review security best practices for production deployments.