This guide covers running and deploying the Open Hardware Manager (OHM) in containerized environments.
- Quick Start
- Configuration
- Usage Modes
- Production Deployment
- Cloud Platform Deployment
- Monitoring and Logging
- Security Considerations
- Troubleshooting
For a pre-built image matching CI and release automation. The :0.8 tag floats
to the latest 0.8.x patch; pin an exact :X.Y.Z tag for reproducibility.
docker pull touchthesun/openhardwaremanager:0.8With local storage (no credentials needed):
docker run -p 8001:8001 \
-e STORAGE_PROVIDER=local \
-e LLM_ENABLED=false \
touchthesun/openhardwaremanager:0.8With remote storage (Azure Blob, AWS S3, or GCS):
Important: The published image does not contain a
.envfile. Storage credentials set only in a local.envare not visible to the container — they must be passed explicitly at runtime.
The recommended approach is --env-file, which mirrors what docker-compose does via its env_file: directive:
docker run -p 8001:8001 \
--env-file .env \
touchthesun/openhardwaremanager:0.8You can also pass individual variables with -e flags:
docker run -p 8001:8001 \
-e STORAGE_PROVIDER=azure_blob \
-e AZURE_STORAGE_ACCOUNT=<account-name> \
-e AZURE_STORAGE_KEY=<account-key> \
-e AZURE_STORAGE_CONTAINER=<container-name> \
touchthesun/openhardwaremanager:0.8- API documentation: http://localhost:8001/v1/docs
- Health check: http://localhost:8001/health (the
versionfield reports the running release) - Federation is off by default (
OHM_FEDERATION_ENABLED=false). See federation infrastructure to enable peer sync.
Other tags: touchthesun/openhardwaremanager:0.8, :latest. Images are multi-arch (linux/amd64, linux/arm64). See Release process.
-
Clone and navigate to the project:
cd supply-graph-ai -
Copy the environment template:
cp env.template .env
-
Edit the
.envfile with your configuration:nano .env # or your preferred editor -
Start the API server:
docker-compose up ohm-api
-
Access the API:
- API Documentation: http://localhost:8001/docs
- Health Check: http://localhost:8001/health
- API Base URL: http://localhost:8001/v1
-
Build the image (uses frozen
uv.lock; pass version for labels):docker build --build-arg APP_VERSION=0.8.0 -t supply-graph-ai:0.8.0 . -
Run the API server:
docker run -p 8001:8001 \ -e API_KEYS="your-api-key" \ -v $(pwd)/storage:/app/storage \ -v $(pwd)/logs:/app/logs \ supply-graph-ai api
-
Run CLI commands:
docker run --rm \ -v $(pwd)/storage:/app/storage \ -v $(pwd)/test-data:/app/test-data \ supply-graph-ai cli okh validate /app/test-data/manifest.okh.json
The container supports configuration through environment variables. See env.template for a complete list of available options.
API_HOST: API server host (default:0.0.0.0)API_PORT: API server port (default:8001)API_KEYS: Comma-separated list of API keys for authenticationLOG_LEVEL: Logging level (default:INFO)DEBUG: Enable debug mode (default:false)
STORAGE_PROVIDER selects the backend (default: local). The credential variables required depend on the provider:
Local storage (default, no credentials needed):
STORAGE_PROVIDER=local
LOCAL_STORAGE_PATH=storage # path inside the container; mount a volume here for persistence
Azure Blob Storage:
STORAGE_PROVIDER=azure_blob
AZURE_STORAGE_ACCOUNT=<storage-account-name>
AZURE_STORAGE_KEY=<storage-account-key>
AZURE_STORAGE_CONTAINER=<container-name>
AWS S3:
STORAGE_PROVIDER=aws_s3
AWS_ACCESS_KEY_ID=<access-key-id>
AWS_SECRET_ACCESS_KEY=<secret-access-key>
AWS_S3_BUCKET=<bucket-name>
AWS_DEFAULT_REGION=us-east-1 # optional, defaults to us-east-1
Google Cloud Storage:
STORAGE_PROVIDER=gcs
GCP_PROJECT_ID=<project-id>
GCP_CREDENTIALS_JSON=<path-to-service-account-json-or-json-string>
GCP_STORAGE_BUCKET=<bucket-name>
None of these are baked into the published image. Pass them via
--env-file .envor-e KEY=VALUEflags. When usingdocker-composefrom source,env_file: - .envindocker-compose.ymlhandles this automatically.
LLM_ENABLED: Enable LLM integration (default: false)LLM_PROVIDER: LLM provider (openai, anthropic, google, azure, local)LLM_MODEL: Specific model to useLLM_QUALITY_LEVEL: Quality level (hobby, professional, medical)
The container expects the following volume mounts:
/app/storage: Persistent storage directory/app/logs: Log files directory/app/test-data: Test data directory (optional)
Start the FastAPI server:
docker run -p 8001:8001 supply-graph-ai apiRun CLI commands:
# Show CLI help
docker run --rm supply-graph-ai cli --help
# Validate an OKH file
docker run --rm \
-v $(pwd)/test-data:/app/test-data \
supply-graph-ai cli okh validate /app/test-data/manifest.okh.json
# List packages
docker run --rm supply-graph-ai cli package list
# Run matching
docker run --rm \
-v $(pwd)/test-data:/app/test-data \
supply-graph-ai cli match okh /app/test-data/manifest.okh.json-
Build production image:
docker build -t ohm-prod . -
Run with production settings:
docker run -d \ --name ohm-api \ -p 8001:8001 \ -e API_KEYS="your-production-api-key" \ -e LOG_LEVEL="INFO" \ -e STORAGE_PROVIDER="aws_s3" \ -e AWS_S3_BUCKET="your-bucket" \ -v ohm-storage:/app/storage \ -v ohm-logs:/app/logs \ ohm-prod
Create a docker-compose.prod.yml:
version: '3.8'
services:
ohm-api:
build:
context: .
dockerfile: Dockerfile
container_name: ohm-api-prod
ports:
- "8001:8001"
environment:
- API_HOST=0.0.0.0
- API_PORT=8001
- LOG_LEVEL=INFO
- DEBUG=false
- API_KEYS=${API_KEYS}
- STORAGE_PROVIDER=${STORAGE_PROVIDER}
- STORAGE_BUCKET_NAME=${STORAGE_BUCKET_NAME}
- LLM_ENABLED=${LLM_ENABLED}
- LLM_PROVIDER=${LLM_PROVIDER}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
volumes:
- ohm-storage:/app/storage
- ohm-logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
ohm-storage:
ohm-logs:Deploy with:
docker-compose -f docker-compose.prod.yml up -d-
Build and push image:
gcloud builds submit --tag gcr.io/PROJECT_ID/supply-graph-ai
-
Deploy to Cloud Run:
gcloud run deploy supply-graph-ai \ --image gcr.io/PROJECT_ID/supply-graph-ai \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --port 8001 \ --memory 4Gi \ --cpu 2 \ --max-instances 10 \ --set-env-vars="API_KEYS=your-api-key,STORAGE_PROVIDER=gcp_storage"
-
Create ECS task definition:
{ "family": "supply-graph-ai", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "1024", "memory": "2048", "executionRoleArn": "arn:aws:iam::ACCOUNT:role/ecsTaskExecutionRole", "containerDefinitions": [{ "name": "ohm-api", "image": "ACCOUNT.dkr.ecr.REGION.amazonaws.com/supply-graph-ai:latest", "portMappings": [{ "containerPort": 8001, "protocol": "tcp" }], "environment": [ {"name": "API_KEYS", "value": "your-api-key"}, {"name": "STORAGE_PROVIDER", "value": "aws_s3"}, {"name": "AWS_S3_BUCKET", "value": "your-bucket"} ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/supply-graph-ai", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } } }] } -
Create ECS service:
aws ecs create-service \ --cluster your-cluster \ --service-name ohm-api \ --task-definition supply-graph-ai \ --desired-count 2 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[subnet-12345],securityGroups=[sg-12345],assignPublicIp=ENABLED}"
- Deploy with Azure CLI:
az container create \ --resource-group myResourceGroup \ --name ohm-api \ --image your-registry.azurecr.io/supply-graph-ai:latest \ --cpu 2 \ --memory 4 \ --ports 8001 \ --environment-variables \ API_KEYS=your-api-key \ STORAGE_PROVIDER=azure_blob \ AZURE_STORAGE_ACCOUNT=your-account \ --registry-login-server your-registry.azurecr.io \ --registry-username your-username \ --registry-password your-password
-
Apply Kubernetes manifests:
kubectl apply -f k8s-deployment.yaml
-
Check deployment status:
kubectl get pods -n ohm kubectl get services -n ohm kubectl get ingress -n ohm
-
Access the application:
kubectl port-forward -n ohm service/ohm-api-service 8001:80
The application provides several health check endpoints:
GET /health- Basic health checkGET /- API information and status
Configure logging through environment variables:
LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR
LOG_FILE=logs/app.logAdd Prometheus metrics endpoint:
# In your FastAPI app
from prometheus_fastapi_instrumentator import Instrumentator
instrumentator = Instrumentator()
instrumentator.instrument(app).expose(app)For production, consider using:
- ELK Stack (Elasticsearch, Logstash, Kibana)
- Fluentd for log collection
- CloudWatch (AWS) or Stackdriver (GCP) for cloud logging
-
Use strong API keys:
API_KEYS="$(openssl rand -hex 32),$(openssl rand -hex 32)" -
Enable HTTPS in production:
- Use reverse proxy (nginx, traefik)
- Configure SSL certificates
- Set secure headers
-
Network security:
- Use private networks where possible
- Configure firewall rules
- Implement rate limiting
- Use non-root user (already configured)
- Scan images for vulnerabilities:
docker scan supply-graph-ai
- Keep base images updated
- Use secrets management for sensitive data
- Encrypt data at rest
- Use secure storage backends
- Implement data retention policies
- Regular security audits
-
Container won't start:
docker logs <container-id>
-
API not accessible:
- Check port mapping
- Verify firewall settings
- Check container health
-
Storage falling back to local unexpectedly, or DNS failure on the Azure/S3 hostname:
Two related causes:
a) Credentials not reaching the container. When running the published image directly (not via
docker-compose), the.envfile on your host is invisible to the container.b) Quoted values in
.envpassed viadocker run --env-file.docker run --env-filepasses values verbatim — if your.envhasAZURE_STORAGE_ACCOUNT="myaccount", the variable will contain the literal quote characters, producing a URL likehttps://"myaccount".blob.core.windows.netand a DNS failure.docker-compose(via python-dotenv) strips surrounding quotes automatically. As of0.8.1,storage_config.pystrips quotes defensively; if you're on an older image, remove the quotes from the values in your.env.Verify which provider the container is actually using:
docker logs <container-id> 2>&1 | grep -i "storage provider\|storage_provider\|azure\|bucket"
Fix by passing the env file explicitly:
docker run -p 8001:8001 --env-file .env touchthesun/openhardwaremanager:0.8
To verify connectivity once the container is running, use the
storage setupCLI command (it will connect to the configured provider and report any credential errors):docker run --rm --env-file .env \ touchthesun/openhardwaremanager:0.8 \ cli storage setup --provider azure_blob
From source (outside Docker),
scripts/validate_okw_in_storage.pyreads the configured storage and reports what it finds — a quick way to confirm the configuration is correct:uv run python scripts/validate_okw_in_storage.py
-
Volume mounts / permissions:
- Verify volume mounts with
docker inspect <container-id> - Check storage provider credentials
- Ensure proper file permissions on mounted paths
- Verify volume mounts with
-
Memory issues:
- Monitor memory usage
- Adjust container limits
- Check for memory leaks
Enable debug mode for troubleshooting:
docker run -e DEBUG=true -e LOG_LEVEL=DEBUG supply-graph-ai api-
Monitor resource usage:
docker stats <container-id>
-
Scale horizontally:
docker-compose up --scale ohm-api=3
- Use environment-specific configurations
- Implement proper logging and monitoring
- Regular security updates
- Backup strategies for persistent data
- Disaster recovery planning
- Performance testing and optimization
For deployment issues:
- Check the logs for error messages
- Verify environment variable configuration
- Ensure all required volumes are mounted
- Check network connectivity for external services
- Review the troubleshooting section above