Deploy ONEVO to a single GPU Azure VM with Docker Compose, built and released by GitHub Actions.
| Component | Where it runs |
|---|---|
| Backend, dashboard, cloud-ai, Postgres, Redis, MinIO | Azure GPU VM (Docker Compose) |
| Windows connector | Shop PCs — downloaded from dashboard after login |
| CI/CD | GitHub Actions or Jenkins → ACR or VM build → SSH deploy to VM |
Shop staff download ONEVO-Connector-Setup-*.exe from Get started / Admin / Setup in the dashboard. The backend serves the file from /opt/onevo/app/installer-site/ on the VM (mounted into the backend container). The connector service itself does not run in Azure.
Already provisioned for this subscription — see infra/mvp/PROVISIONED.md for live resource names, VM IP, and GitHub secret mapping.
To provision from scratch (another subscription/region):
az login
export AZURE_RESOURCE_GROUP=onevo-mvp-rg
export AZURE_LOCATION=eastus
export AZURE_ACR_NAME=onevoacrmvp # must be globally unique
export AZURE_VM_NAME=onevo-mvp-vm
./infra/mvp/provision-azure.shIf GPU quota is unavailable, use CPU VM (Standard_D2s_v5) and CLOUD_AI_DEVICE=cpu in .env until quota is approved.
SSH to the VM and bootstrap:
ssh azureuser@<VM_PUBLIC_IP>
git clone <your-repo-url> /opt/onevo/app
cd /opt/onevo/app
sudo ACR_LOGIN_SERVER=<acr>.azurecr.io bash infra/mvp/vm-setup.shEdit /etc/nginx/sites-available/onevo — replace YOUR_APP_DOMAIN / YOUR_API_DOMAIN, reload nginx, then:
sudo certbot --nginx -d app.yourdomain.example -d api.yourdomain.exampleCopy and fill secrets:
cp infra/mvp/.env.production.example /opt/onevo/app/.env
nano /opt/onevo/app/.envCreate a production environment in GitHub (Settings → Environments → production) and add:
| Secret | Description |
|---|---|
AZURE_CREDENTIALS |
JSON service principal (see below) |
ACR_NAME |
Registry name (e.g. onevoacr) |
ACR_LOGIN_SERVER |
e.g. onevoacr.azurecr.io |
ACR_USERNAME |
az acr credential show -n <acr> --query username -o tsv |
ACR_PASSWORD |
ACR admin password |
VM_HOST |
VM public IP or DNS |
VM_USER |
SSH user (e.g. azureuser or onevo) |
VM_SSH_KEY |
Private key matching VM authorized_keys |
PRODUCTION_ENV |
Full multiline .env body (no ONEVO_*_IMAGE lines — CI appends those) |
BACKEND_PUBLIC_URL |
https://api.yourdomain.example — for smoke test + installer bake |
SUB=$(az account show --query id -o tsv)
az ad sp create-for-rbac \
--name onevo-github-actions \
--role contributor \
--scopes /subscriptions/$SUB/resourceGroups/onevo-mvp-rg \
--sdk-authPaste the JSON output into AZURE_CREDENTIALS. Grant the SP AcrPush on the registry:
ACR_ID=$(az acr show -n onevoacr --query id -o tsv)
SP_ID=$(az ad sp list --display-name onevo-github-actions --query "[0].id" -o tsv)
az role assignment create --assignee $SP_ID --role AcrPush --scope $ACR_IDRuns on every PR and push to main:
dotnet build(backend)pytest(connector)npm run build(dashboard)- Docker build (no push)
Runs on push to main (with production environment approval if configured):
- Build and push
onevo-backend,onevo-dashboard,onevo-cloud-aito ACR - Rsync repo to VM, write
.env, runinfra/mvp/deploy.sh - Smoke test
GET /api/health - Windows job: build installer EXE, upload artifact, copy to VM
connector/dist/
Manual deploy: Actions → Deploy MVP (Azure) → Run workflow.
- GPU VM + ACR provisioned,
vm-setup.shcompleted - DNS:
app.*→ dashboard (port 4200 via nginx),api.*→ backend (8081) - TLS certificates (certbot)
- GitHub production secrets configured
- Push to
main— deploy workflow green - Login as Admin → Get started → installer download works
- Shop PC: run EXE, setup code, RTSP URLs
- Shop PC:
Test-NetConnection <VM_IP> -Port 9000succeeds (MinIO clip uploads) - Connector online on Setup page
- Test alert + email (
SMTP_ENABLE=true)
| Issue | Fix |
|---|---|
| GPU quota denied | Use CPU VM or request quota increase; set CLOUD_AI_DEVICE=cpu in .env |
| ACR pull 401 on VM | Check ACR_* in .env; run docker login manually on VM |
| Installer 404 | Ensure build-installer job succeeded and EXE exists in /opt/onevo/app/installer-site/ |
| CORS errors | Set CORS_ORIGINS=https://app.yourdomain.example in .env |
| Deploy SSH fails | Verify VM_SSH_KEY, NSG allows SSH from GitHub Actions IPs (or use self-hosted runner in same VNet) |
Clip upload timeout (:9000) |
NSG must allow 9000; set S3_PUBLIC_ENDPOINT=http://<VM_IP>:9000 in .env; test curl http://<VM_IP>:9000/minio/health/live from shop PC |
Connector disk_critical on shop PC |
Free C: drive space; clear %ProgramData%\ONEVO\Connector\data\clips |
| Path | Purpose |
|---|---|
infra/mvp/provision-azure.sh |
Create RG, ACR, VM |
infra/mvp/vm-setup.sh |
Docker, NVIDIA toolkit, nginx on VM |
infra/mvp/deploy.sh |
Pull ACR images or local build; restart compose |
scripts/deploy-vm.ps1 |
Jenkins / manual deploy from Windows |
Jenkinsfile |
Jenkins pipeline definition |
docs/JENKINS_DEPLOY.md |
Jenkins setup on Windows |
infra/mvp/nginx-host.conf |
Host TLS reverse proxy template |
infra/mvp/.env.production.example |
Production env template |
docker-compose.acr.yml |
Use pre-built images from ACR |
When outgrowing a single VM: Azure Database for PostgreSQL, Azure Cache for Redis, Blob Storage instead of MinIO, AKS or Container Apps with GPU node pool.