Deploy iCommerce on Hetzner
Run StateSet iCommerce 24/7 for approximately $5/month on Hetzner. This guide walks you through deploying a persistent iCommerce Gateway on a Hetzner VPS using Docker, with durable state, baked-in binaries, and safe restart behavior.
Goal
Deploy a production-ready StateSet iCommerce Gateway on Hetzner with:
- Persistent configuration and workspace data
- Docker-based isolated runtime
- SSH tunnel access for secure administration
- Automatic restart on failure
Hetzner offers some of the most cost-effective VPS options available. Pick the smallest Debian/Ubuntu VPS that fits your workload and scale up if you encounter out-of-memory errors.
What you’ll build
Provision Hetzner VPS
Create a small Linux server with root access.
Install Docker runtime
Install Docker for isolated, reproducible application runtime.
Configure persistent storage
Mount host directories for configuration and workspace data that survives restarts.
Deploy the Gateway
Build and launch the iCommerce Gateway with Docker Compose.
Access securely
Connect via SSH tunnel from your local machine.
Prerequisites
Before you begin, ensure you have:
- Hetzner VPS with root access
- SSH access from your local machine
- Basic familiarity with terminal commands
- StateSet API credentials
- Model provider credentials (OpenAI, Anthropic, etc.)
Optional integrations:
- WhatsApp Business API credentials
- Telegram bot token
- Gmail OAuth credentials
This guide assumes Ubuntu or Debian on Hetzner. If you’re on another Linux VPS provider, map packages accordingly.
Quick path (experienced operators)
If you’re familiar with Hetzner and Docker, follow this condensed workflow:
- Provision Hetzner VPS (Ubuntu/Debian)
- Install Docker
- Clone the StateSet iCommerce repository
- Create persistent host directories
- Configure
.env and docker-compose.yml
- Bake required binaries into the image
docker compose up -d
- Verify persistence and Gateway access
1) Provision the VPS
Create an Ubuntu or Debian VPS in the Hetzner Cloud Console.
Recommended specifications
| Type | Specs | Cost | Notes |
|---|
| CX22 | 2 vCPU, 4GB RAM | ~€4/mo | Recommended |
| CX11 | 1 vCPU, 2GB RAM | ~€3/mo | Budget option, may OOM under load |
Connect as root:
This guide assumes the VPS is stateful. Do not treat it as disposable infrastructure.
2) Install Docker
Run the following commands on the VPS:
apt-get update
apt-get install -y git curl ca-certificates
curl -fsSL https://get.docker.com | sh
Verify the installation:
docker --version
docker compose version
3) Clone the repository
git clone https://github.com/stateset/stateset-icommerce.git
cd stateset-icommerce
4) Create persistent host directories
Docker containers are ephemeral. All long-lived state must live on the host to survive restarts and rebuilds.
mkdir -p /root/.stateset
mkdir -p /root/.stateset/workspace
# Set ownership to the container user (uid 1000)
chown -R 1000:1000 /root/.stateset
chown -R 1000:1000 /root/.stateset/workspace
Setting the correct ownership is critical. The container runs as uid 1000 (node user), so the host directories must be writable by that user.
Create a .env file in the repository root:
STATESET_IMAGE=stateset-icommerce:latest
STATESET_GATEWAY_TOKEN=change-me-now
STATESET_GATEWAY_BIND=lan
STATESET_GATEWAY_PORT=18789
STATESET_CONFIG_DIR=/root/.stateset
STATESET_WORKSPACE_DIR=/root/.stateset/workspace
STATESET_KEYRING_PASSWORD=change-me-now
XDG_CONFIG_HOME=/home/node/.stateset
Generate strong secrets:
Do not commit the .env file to version control. It contains sensitive credentials.
6) Docker Compose configuration
Create or update docker-compose.yml:
services:
icommerce-gateway:
image: ${STATESET_IMAGE}
build: .
restart: unless-stopped
env_file:
- .env
environment:
- HOME=/home/node
- NODE_ENV=production
- TERM=xterm-256color
- STATESET_GATEWAY_BIND=${STATESET_GATEWAY_BIND}
- STATESET_GATEWAY_PORT=${STATESET_GATEWAY_PORT}
- STATESET_GATEWAY_TOKEN=${STATESET_GATEWAY_TOKEN}
- STATESET_KEYRING_PASSWORD=${STATESET_KEYRING_PASSWORD}
- XDG_CONFIG_HOME=${XDG_CONFIG_HOME}
- PATH=/home/linuxbrew/.linuxbrew/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
volumes:
- ${STATESET_CONFIG_DIR}:/home/node/.stateset
- ${STATESET_WORKSPACE_DIR}:/home/node/.stateset/workspace
ports:
# Keep the Gateway loopback-only; access via SSH tunnel
- "127.0.0.1:${STATESET_GATEWAY_PORT}:18789"
command:
[
"node",
"dist/index.js",
"gateway",
"--bind",
"${STATESET_GATEWAY_BIND}",
"--port",
"${STATESET_GATEWAY_PORT}"
]
To expose the Gateway publicly, remove the 127.0.0.1: prefix from the port mapping and configure firewall rules accordingly. See the security documentation for guidance.
7) Bake required binaries into the image
Installing binaries inside a running container is a common mistake. Anything installed at runtime will be lost on restart. All external binaries required by skills must be installed at image build time.
If you add new skills later that depend on additional binaries, you must:
- Update the Dockerfile
- Rebuild the image
- Restart the containers
Example Dockerfile
FROM node:22-bookworm
RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/*
# Gmail CLI
RUN curl -L https://github.com/steipete/gog/releases/latest/download/gog_Linux_x86_64.tar.gz \
| tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/gog
# Google Places CLI
RUN curl -L https://github.com/steipete/goplaces/releases/latest/download/goplaces_Linux_x86_64.tar.gz \
| tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/goplaces
# WhatsApp CLI
RUN curl -L https://github.com/steipete/wacli/releases/latest/download/wacli_Linux_x86_64.tar.gz \
| tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/wacli
# Add more binaries as needed using the same pattern
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY scripts ./scripts
RUN corepack enable
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
RUN pnpm ui:install
RUN pnpm ui:build
ENV NODE_ENV=production
CMD ["node","dist/index.js"]
8) Build and launch
docker compose build
docker compose up -d icommerce-gateway
Verify binaries are installed:
docker compose exec icommerce-gateway which gog
docker compose exec icommerce-gateway which goplaces
docker compose exec icommerce-gateway which wacli
Expected output:
/usr/local/bin/gog
/usr/local/bin/goplaces
/usr/local/bin/wacli
9) Verify the Gateway
Check the logs:
docker compose logs -f icommerce-gateway
Success output:
[gateway] listening on ws://0.0.0.0:18789
10) Access from your local machine
Create an SSH tunnel to forward the Gateway port:
ssh -N -L 18789:127.0.0.1:18789 root@YOUR_VPS_IP
Open in your browser:
Enter your gateway token to authenticate.
The -N flag tells SSH not to execute a remote command, making it ideal for port forwarding only.
Persistence reference
All long-lived state must survive restarts, rebuilds, and reboots. Docker is not the source of truth.
| Component | Location | Persistence | Notes |
|---|
| Gateway config | /home/node/.stateset/ | Host volume mount | Includes tokens, settings |
| Model auth profiles | /home/node/.stateset/ | Host volume mount | OAuth tokens, API keys |
| Skill configs | /home/node/.stateset/skills/ | Host volume mount | Skill-level state |
| Agent workspace | /home/node/.stateset/workspace/ | Host volume mount | Code and agent artifacts |
| WhatsApp session | /home/node/.stateset/ | Host volume mount | Preserves QR login |
| Keyring | /home/node/.stateset/ | Host volume + password | Requires STATESET_KEYRING_PASSWORD |
| External binaries | /usr/local/bin/ | Docker image | Must be baked at build time |
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
| OS packages | Container filesystem | Docker image | Do not install at runtime |
Updates
To update StateSet iCommerce on the VPS:
cd ~/stateset-icommerce
git pull
docker compose build
docker compose up -d
Troubleshooting
SSH connection refused
Verify the VPS is running and your IP is not blocked by any firewall rules.
# Check if SSH is listening
netstat -tlnp | grep 22
Out of memory (OOM)
If hitting OOM on a smaller VPS, upgrade to a larger instance:
- Create a snapshot of your VPS in Hetzner Cloud Console
- Resize or create a new VPS with more resources
- Restore from snapshot if needed
Container fails to start
Check logs for errors:
docker compose logs icommerce-gateway
Verify environment variables are set correctly:
Permission denied on mounted volumes
Ensure the host directories have the correct ownership:
chown -R 1000:1000 /root/.stateset
Firewall blocking connections
If using Hetzner’s firewall, ensure the required ports are open:
# Check current firewall rules
ufw status
# Allow SSH (if using ufw)
ufw allow 22/tcp
Hetzner-specific tips
Enable automatic backups
Enable automatic backups in the Hetzner Cloud Console for disaster recovery. Cost is approximately 20% of VPS price.
Use a floating IP
For production deployments, assign a floating IP to your VPS. This allows you to migrate to a new server without changing your IP address.
Set up monitoring
Hetzner provides basic monitoring in the Cloud Console. For more detailed metrics, consider installing a monitoring agent:
# Example: Install Netdata for real-time monitoring
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
Next steps