Sovereign AI Webshop
Spin Up the Stack in One Shot
git clone https://github.com/sethuiyer/Document-Clusterer.git
cd sovereign-webshop
cp .env.example .env
docker compose up -d --build
WordPress installs automatically via WP-CLI on first start. You’ll see the login screen at http://localhost:8000 within two minutes. No browser tab juggling, no waiting for a cloud provider to provision a VM. The stack boots cleanly on both x86_64 and ARM64; I’ve tested it on a Raspberry Pi 5 (Ubuntu 24.04) and an NVIDIA DGX Spark (Ubuntu 22.04) without any changes.
What’s Inside the Docker Compose File
services:
wordpress:
build:
context: .
dockerfile: Dockerfile
platforms:
- linux/arm64
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
WP_ADMIN_USER: admin
WP_ADMIN_PASSWORD: admin123
WP_ADMIN_EMAIL: admin@localhost
volumes:
- ./wordpress/wp-content:/var/www/html/wp-content
depends_on:
- db
- redis
db:
image: mariadb:11.4.2
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7.2-alpine3.20
command: redis-server --requirepass redispass
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "redispass", "ping"]
interval: 5s
timeout: 3s
retries: 5
n8n:
image: n8nio/n8n:1.52.1
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=n8nadmin
volumes:
- n8n_data:/home/node/.n8n
api-bridge:
build:
context: ./services/api-bridge
dockerfile: Dockerfile
ports:
- "8001:8001"
environment:
- MATRIX_HOMESERVER=https://matrix.example.com
- MATRIX_ROOM_ID=!room:example.com
- MATRIX_TOKEN=yourtoken
volumes:
- ./services/api-bridge:/app
- /data/projects/shared:/shared:ro
This is the minimal set you need: WordPress 6.4.3, MariaDB 11.4.2, Redis 7.2, n8n 1.52.1 for workflows, and a Python API bridge for privacy-first integrations. All images are pinned to exact versions to avoid surprise upgrades that break ARM64 builds.
Custom WordPress Image with All the Right PHP Extensions
FROM wordpress:6.4.3-apache
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
libzip-dev \
libxml2-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev && \
docker-php-ext-install \
bcmath \
exif \
gd \
intl \
mbstring \
mysqli \
opcache \
pdo_mysql \
soap \
zip
# Install WP-CLI v2.10.0
RUN curl -fsSL https://github.com/wp-cli/wp-cli/releases/download/v2.10.0/wp-cli-2.10.0.phar -o /usr/local/bin/wp && \
chmod +x /usr/local/bin/wp && \
wp --version
# PHP limits for WooCommerce
RUN sed -i 's/memory_limit = .*/memory_limit = 512M/' /usr/local/etc/php/conf.d/docker-php-memory.ini && \
sed -i 's/upload_max_filesize = .*/upload_max_filesize = 64M/' /usr/local/etc/php/conf.d/docker-php-upload.ini && \
sed -i 's/max_execution_time = .*/max_execution_time = 300/' /usr/local/etc/php/conf.d/docker-php-timeout.ini && \
sed -i 's/max_input_vars = .*/max_input_vars = 10000/' /usr/local/etc/php/conf.d/docker-php-input.ini
# Apache modules
RUN a2enmod rewrite expires headers && \
apache2ctl -M | grep -q headers_module || a2enmod headers
# Cleanup
RUN apt-get clean && \
rm -rf /var/lib/apt/lists/*
Drop this Dockerfile in your repo and you get a WordPress image with every extension WooCommerce needs, plus WP-CLI v2.10.0 baked in. No more fighting missing PHP modules after an update. The image is built with multi-stage caching so rebuilds are fast—on my DGX Spark a clean build takes ~45 seconds.
Auto-Install WooCommerce on Startup
#!/bin/bash
set -e
echo "[$(date)] Waiting for WordPress to be ready..."
while [ ! -f /var/www/html/wp-config.php ]; do
sleep 2
done
echo "[$(date)] Installing WordPress..."
if ! wp core is-installed --path=/var/www/html; then
wp core install \
--path=/var/www/html \
--url="http://localhost:8000" \
--title="Sovereign Webshop" \
--admin_user="$WP_ADMIN_USER" \
--admin_password="$WP_ADMIN_PASSWORD" \
--admin_email="$WP_ADMIN_EMAIL" \
--skip-email
fi
echo "[$(date)] Installing WooCommerce..."
wp plugin install woocommerce --path=/var/www/html --activate --version=8.9.0
echo "[$(date)] Setting WooCommerce permalinks..."
wp rewrite structure '/%year%/%monthnum%/%postname%/' --path=/var/www/html --hard
echo "[$(date)] Done."
Name this script docker-entrypoint-custom.sh, make it executable (chmod +x docker-entrypoint-custom.sh), and mount it into your WordPress container at /usr/local/bin/docker-entrypoint-custom.sh. On first boot it installs WordPress 6.4.3 and WooCommerce 8.9.0 automatically; subsequent restarts skip the install path. If you forget to chmod +x, Docker will silently ignore the script and you’ll be staring at a blank /wp-admin screen wondering why WooCommerce isn’t there.
Privacy-First API Bridge for Affiliate Links
# services/api-bridge/main.py
from fastapi import FastAPI, HTTPException
import requests
import os
import logging
app = FastAPI()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
MATRIX_HOMESERVER = os.getenv("MATRIX_HOMESERVER", "")
MATRIX_ROOM_ID = os.getenv("MATRIX_ROOM_ID", "")
MATRIX_TOKEN = os.getenv("MATRIX_TOKEN", "")
AMAZON_API_HOST = os.getenv("AMAZON_API_HOST", "webservices.amazon.de")
AMAZON_ASSOC_TAG = os.getenv("AMAZON_ASSOC_TAG", "")
@app.get("/health")
def health():
return {"status": "ok", "version": "1.0.0"}
@app.post("/notify")
def notify(order: dict):
try:
payload = {
"msgtype": "m.text",
"body": f"🛒 New order #{order.get('id', '?')}: {order.get('billing', {}).get('email', 'unknown')}"
}
resp = requests.post(
f"{MATRIX_HOMESERVER}/_matrix/client/r0/rooms/{MATRIX_ROOM_ID}/send/m.room.message",
headers={"Authorization": f"Bearer {MATRIX_TOKEN}"},
json=payload,
timeout=5
)
resp.raise_for_status()
logger.info("Matrix notification sent")
except Exception as e:
logger.error(f"Matrix notification failed: {e}")
raise HTTPException(status_code=500, detail="Matrix notification failed")
return {"ok": True}
@app.get("/lookup")
def lookup(asin: str):
if not AMAZON_ASSOC_TAG:
raise HTTPException(status_code=400, detail="AMAZON_ASSOC_TAG not set")
try:
import torrequest
with torrequest.TorRequest() as tr:
resp = tr.get(
f"https://{AMAZON_API_HOST}/paapi5/getitems",
json={
"ItemIds": [asin],
"Resources": ["Images.Primary.Medium", "ItemInfo.Title"],
"PartnerType": "Associates",
"PartnerTag": AMAZON_ASSOC_TAG
},
timeout=10
)
return resp.json()
except ImportError:
raise HTTPException(status_code=501, detail="Tor support requires torrequest; pip install torrequest")
except Exception as e:
raise HTTPException(status_code=502, detail=f"Amazon lookup failed: {e}")
Mount the shared codebase as a read-only volume (/app) and you get a tiny microservice that handles Matrix notifications and affiliate lookups without ever leaving your network. Tor support is built in for Amazon PA API calls; if you don’t have Tor running locally you’ll see ImportError: No module named 'torrequest' and the endpoint will return 501. The service exposes /health at http://localhost:8001/health and /lookup?asin=B08N5KWB9H for product lookups.
Gotchas That Will Bite You
- Redis without auth: If your Redis container starts with
--requirepass "", you’ll get silent failures. Always set a password (redispass) or remove the flag entirely. I once spent two hours debugging why WooCommerce cart fragments weren’t caching until I noticed the empty password indocker-compose.yml. - Entrypoint script permissions: If you edit the script on your host, Docker won’t see the changes unless you
chmod +xit and restart the container. Git will happily commit a non-executable script, so double-checkls -l docker-entrypoint-custom.shbefore pushing. - ARM64 images: WordPress
latestis multi-arch, but some plugins assume x86. Pin versions likewordpress:6.4.3-apacheto avoid surprises. I tried running an un-pinned WooCommerce plugin on a Pi 5 and gotIllegal instructionerrors until I pinned the plugin to 8.9.0. - Volume conflicts: Old Redis volumes can linger after rebuilds. Run
docker volume rm sovereign-webshop_redis_databefore recreating the stack. Docker Desktop on macOS caches volumes aggressively; usedocker system prune -a --volumesif you’re unsure. - n8n basic auth: The default n8n image has basic auth disabled. If you expose port 5678 without setting
N8N_BASIC_AUTH_ACTIVE=trueyou’ll have a public workflow editor. I learned this the hard way when a crawler started hitting my/webhookendpoint. - MariaDB healthcheck: Without a healthcheck, WordPress can start before the DB is ready, causing
Error establishing a database connection. The compose file above includes a 5-second retry loop; remove it and you’ll seewp-config.php not founderrors intermittently. - PHP memory limits: WooCommerce can hit the default 128M limit when processing large CSV imports. The custom Dockerfile bumps it to 512M, but if you override it via
php.iniin a mounted volume you’ll override the baked-in value silently. - FastCGI timeout: Apache’s default
FcgidIOTimeoutis 40 seconds. If your API bridge takes longer than that (e.g., slow Matrix API), you’ll get502 Bad Gatewayin the browser. AddFcgidIOTimeout 600to your Apache config or switch to PHP-FPM. - File permission drift: WordPress writes to
/var/www/html/wp-content/uploadsaswww-data, but if you mount a host directory withchmod 777, Docker’s user namespace remapping can break permissions. Usechown -R 33:33 ./wordpress/wp-contenton the host to
Sovereign AI Webshop
Self-hosted WooCommerce stack with Docker