CommerceDeployment
Self-Hosted
Deploy Hanzo Commerce on bare metal or a VPS with systemd and Nginx
Deploy Hanzo Commerce directly on a Linux server without container orchestration. This guide covers binary installation, systemd service configuration, Nginx reverse proxy, SSL with Let's Encrypt, and backup strategy.
Prerequisites
- A Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
- Root or sudo access
- A domain name pointing to your server's IP
- PostgreSQL 15+ (local or managed)
- Redis 7+ (local or managed)
System Requirements
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB SSD | 100 GB SSD |
| OS | Ubuntu 22.04 / Debian 12 | Ubuntu 24.04 |
Install Dependencies
# Update system
sudo apt update && sudo apt upgrade -y
# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# Install Redis
sudo apt install -y redis-server
# Install Nginx
sudo apt install -y nginx
# Install certbot for Let's Encrypt
sudo apt install -y certbot python3-certbot-nginxConfigure PostgreSQL
sudo -u postgres psql -c "CREATE USER commerce WITH PASSWORD 'your-secure-password';"
sudo -u postgres psql -c "CREATE DATABASE commerce OWNER commerce;"Configure Redis
Edit /etc/redis/redis.conf:
maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly yesRestart Redis:
sudo systemctl restart redis-serverInstall Commerce Binary
Download the latest release:
# Create commerce user
sudo useradd -r -m -d /opt/commerce -s /bin/false commerce
# Download binary
sudo curl -L -o /usr/local/bin/commerce \
"https://github.com/hanzoai/commerce/releases/latest/download/commerce-linux-amd64"
sudo chmod +x /usr/local/bin/commerce
# Create data directory
sudo mkdir -p /opt/commerce/data
sudo chown -R commerce:commerce /opt/commerceVerify the installation:
commerce versionConfiguration
Create the configuration file:
# Server
COMMERCE_DIR=/opt/commerce/data
COMMERCE_ENV=production
COMMERCE_PORT=8090
COMMERCE_HOST=127.0.0.1
COMMERCE_LOG_LEVEL=info
# Database
DATABASE_URL=postgresql://commerce:your-secure-password@localhost:5432/commerce?sslmode=disable
# Cache
KV_URL=redis://localhost:6379/0
# Storage (local filesystem or S3)
S3_URL=file:///opt/commerce/data/uploads
# Search (optional -- install Meilisearch separately if needed)
# SEARCH_URL=http://localhost:7700
# Hanzo Platform
HANZO_API_KEY=hk_live_...
HANZO_CLIENT_ID=your_client_id
HANZO_CLIENT_SECRET=your_client_secret
# Payments
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...Set permissions:
sudo chown commerce:commerce /opt/commerce/.env
sudo chmod 600 /opt/commerce/.envsystemd Service
Create the service file:
[Unit]
Description=Hanzo Commerce API
After=network.target postgresql.service redis-server.service
Requires=postgresql.service redis-server.service
[Service]
Type=simple
User=commerce
Group=commerce
WorkingDirectory=/opt/commerce
EnvironmentFile=/opt/commerce/.env
ExecStart=/usr/local/bin/commerce serve
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/commerce/data
PrivateTmp=true
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=commerce
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable commerce
sudo systemctl start commerceCheck status:
sudo systemctl status commerce
sudo journalctl -u commerce -fNginx Reverse Proxy
Create the Nginx configuration:
upstream commerce_backend {
server 127.0.0.1:8090;
keepalive 32;
}
server {
listen 80;
server_name commerce.example.com;
# Redirect to HTTPS (certbot will modify this)
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name commerce.example.com;
# SSL (certbot will fill these in)
# ssl_certificate /etc/letsencrypt/live/commerce.example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/commerce.example.com/privkey.pem;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Request limits
client_max_body_size 50M;
# Proxy to Commerce
location / {
proxy_pass http://commerce_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# Health check (no access log)
location /health {
proxy_pass http://commerce_backend;
access_log off;
}
# Block direct access to metrics
location /metrics {
deny all;
return 404;
}
}Enable the site:
sudo ln -s /etc/nginx/sites-available/commerce /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxSSL with Let's Encrypt
sudo certbot --nginx -d commerce.example.comCertbot will:
- Obtain a certificate
- Update your Nginx config with SSL paths
- Set up automatic renewal
Verify auto-renewal:
sudo certbot renew --dry-runFirewall
Allow only HTTP, HTTPS, and SSH:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enableBackup Strategy
PostgreSQL Backups
Create a daily backup script:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/commerce/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
mkdir -p "$BACKUP_DIR"
# Dump database
pg_dump -U commerce -Fc commerce > "$BACKUP_DIR/commerce_$TIMESTAMP.dump"
# Remove old backups
find "$BACKUP_DIR" -name "*.dump" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: commerce_$TIMESTAMP.dump"sudo chmod +x /opt/commerce/backup.sh
sudo chown commerce:commerce /opt/commerce/backup.shAdd a cron job:
sudo crontab -u commerce -e# Daily backup at 2:00 AM
0 2 * * * /opt/commerce/backup.sh >> /opt/commerce/backups/backup.log 2>&1Restore from Backup
pg_restore -U commerce -d commerce --clean /opt/commerce/backups/commerce_20260216_020000.dumpUpdating
# Download new binary
sudo curl -L -o /usr/local/bin/commerce \
"https://github.com/hanzoai/commerce/releases/latest/download/commerce-linux-amd64"
sudo chmod +x /usr/local/bin/commerce
# Restart the service (migrations run automatically)
sudo systemctl restart commerce
# Verify
sudo systemctl status commerce
curl -s http://localhost:8090/health | python3 -m json.toolMonitoring
Journal Logs
# Follow logs
sudo journalctl -u commerce -f
# Last hour
sudo journalctl -u commerce --since "1 hour ago"
# Errors only
sudo journalctl -u commerce -p errHealth Check Script
#!/bin/bash
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8090/health/ready)
if [ "$STATUS" != "200" ]; then
echo "Commerce unhealthy (HTTP $STATUS)" | systemd-cat -t commerce-healthcheck -p err
systemctl restart commerce
fiAdd to cron for every 5 minutes:
*/5 * * * * /opt/commerce/healthcheck.shNext Steps
- Set up Docker if you prefer containerized deployments
- Deploy your storefront to Vercel
- Review architecture for scaling beyond a single server
How is this guide?
Last updated on