#
Running Zurg in Google Cloud
Zurg leverages support for .STRM files and WebDAV to create a shareable media library; it pairs very well with Infuse. (This is a low cost, low complexity deployment that is not suited for users of Emby, Jelly, Plex, *arr.)
- Zurg is available in a private repository: debridmediamanager/zurg
- Google Artifact Registry mirrors this private repository to directly deploy the latest image from GitHub Container Registry to Google Cloud Run.
- Google Cloud Run serves Zurg in a low-resource instance using a saved secret in Secret Manager as the configuration file.
#
Prerequisites
- Google Cloud CLI installed and configured.
- GitHub Personal Access Token with the
read:packagesscope to allow Google Cloud to pull images from GitHub Container Registry.
#
Add a private remote repository to Artifact Registry
Cloud Run does not directly support deploying private images from GitHub Container Registry. Private remote remote repositories must instead be mirrored in Artifact Registry.
Create a new project:
gcloud projects create --name="Zurg"Set your active Project ID and set related variables:
gcloud projects list printf "Google Cloud Project ID: "; read PROJECT_ID; export PROJECT_ID; PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") echo Google Cloud Project Number: ${PROJECT_NUMBER} gcloud config set project ${PROJECT_ID}Configure Artifact Registry and Secret Manager:
Enable the API for each service:
gcloud services enable artifactregistry.googleapis.com gcloud services enable secretmanager.googleapis.comAdd your GitHub Personal Access Token to Secret Manager:
read -s -p "GitHub token (hidden): " GITHUB_TOKEN; echo gcloud secrets describe github-token >/dev/null 2>&1 \ || gcloud secrets create github-token --quiet; printf "%s" "$GITHUB_TOKEN" \ | gcloud secrets versions add github-token --data-file=- --quiet unset GITHUB_TOKENGrant Artifact Registry service agent permission to access this secret:
gcloud secrets add-iam-policy-binding github-token \ --member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-artifactregistry.iam.gserviceaccount.com" \ --role="roles/secretmanager.secretAccessor" \ --project=${PROJECT_ID}Create an Artifact Registry remote repository:
Set your GitHub username as a temporary variable:
printf "GitHub username: "; read GITHUB_USERNAME; export GITHUB_USERNAMESelect a Google Cloud Region that meets your low latency or data residency requirements. Set this region (for example
us-central1) as a variable:printf "Google Cloud Region: "; read REGION_ID; export REGION_IDCreate a remote repository named
github-remotethat acts as a pull-through cache for GitHub Container Registry using the token stored in Secret Manager:gcloud artifacts repositories create github-remote \ --repository-format=docker \ --mode=REMOTE_REPOSITORY \ --location=${REGION_ID} \ --description="remote repository for ghcr.io with authentication" \ --remote-repo-config-desc="GitHub Container Registry" \ --remote-docker-repo=https://ghcr.io \ --remote-username=GITHUB_USERNAME \ --remote-password-secret-version=projects/${PROJECT_ID}/secrets/github-token/versions/latestVerify the remote repository:
gcloud artifacts repositories list --location=$REGION_IDConsider disabling vulnerability scans for this project:
Caution
The following disables Container Analysis:
gcloud services disable containeranalysis.googleapis.com \
--project=$PROJECT_ID
You now have a new Google Cloud project with a remote repository in Artifact Registry that securely pulls images from ghcr.io using a GitHub API token stored in Secret Manager.
#
Configure Cloud Run and Deploy Zurg
Set your active Project ID and set related variables:
```bash gcloud projects list printf "Google Cloud Project ID: "; read PROJECT_ID; export PROJECT_ID; PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") echo Google Cloud Project Number: ${PROJECT_NUMBER} gcloud config set project ${PROJECT_ID} ```Initialize Cloud Run:
Enable the API for Cloud Run:
gcloud services enable run.googleapis.comInitialize
zurgas a Cloud Run service with a temporary image (and default service account):gcloud run deploy zurg \ --image=gcr.io/cloudrun/hello \ --platform=managed \ --region=$REGION_ID \ --allow-unauthenticatedConfigure Zurg:
Create or edit
config.ymllocally. Save the contents to a new (or updated) secret in Secret Manager:gcloud secrets describe zurg-config-yaml >/dev/null 2>&1 \ || gcloud secrets create zurg-config-yaml \ --data-file=config.yml \ --replication-policy=user-managed \ --locations=$REGION_ID \ && gcloud secrets versions add zurg-config-yaml \ --data-file=config.ymlGrant permission to Cloud Run service agent to access the secret:
SERVICE_ACCOUNT=$(gcloud run services describe zurg \ --region us-central1 \ --format='value(spec.template.spec.serviceAccountName)') gcloud projects add-iam-policy-binding $PROJECT_NUMBER \ --member="serviceAccount:${SERVICE_ACCOUNT}" \ --role="roles/secretmanager.secretAccessor" \ --condition="expression=true,title=unconditional-access,description=granting permanent access for Cloud Run to read secrets"
#
Deploy Zurg to Cloud Run
Deploy (or update) zurg with the :latest release and serve a saved secret as the configuration file:
gcloud run deploy zurg \
--project=${PROJECT_ID} \
--region=${REGION_ID} \
--image=${REGION_ID}-docker.pkg.dev/${PROJECT_ID}/github-remote/ghcr.io/debridmediamanager/zurg:latest \
--port=9999 \
--memory=128Mi \
--min-instances=1 \
--max-instances=2 \
--allow-unauthenticated \
--no-cpu-boost \
--set-env-vars=LOG_LEVEL=INFO \
--set-secrets=/app/config.yml=projects/${PROJECT_NUMBER}/secrets/zurg-config-yaml:latest \
--args=--config,/app/config.yml
[!warning] You may need to direct all traffic to the latest revision:
gcloud run services update-traffic zurg \
--to-latest --region=$REGION_ID
Note
memory=128Mi set memory to 128 MiB which limits the execution environment to the first generation without setting execution-environment=gen1; the second generation execution-environment=gen2 requires at least 512 MiB.
#
Update Zurg
Artifact Registry remote repositories are pull-through caches: they fetch a tag on first pull and continue to serve a cached copy.
To force Artifact Registry to fetch the :latest version delete the existing :latest tag before deploying to Cloud Run:
gcloud artifacts docker images delete \
${REGION_ID}-docker.pkg.dev/${PROJECT_ID}/github-remote/ghcr.io/debridmediamanager/zurg:latest \
--delete-tags
#
Recent Updates
Set
min-instances=1to avoid cold start delays andmax-instances=2to avoid unexpected billing:gcloud run services update zurg \ --region=$REGION_ID \ --min-instances=1 \ --max-instances=2
#
Future Considerations
- There appears to be no billing impact to having Zurg make a basic API call every 15 seconds (default).
- Longterm testing is required to determine to what extent
memory=528Miandexecution-environment=gen2would impact billing.