Skip to main content
Automating deployments means every push to your main branch updates your CVM without manual intervention. This guide covers four approaches: the Phala CLI in GitHub Actions, the JS SDK in GitHub Actions, a GitLab CI pipeline, and Terraform in CI.

Prerequisites

  • A Git repository with your application code and a docker-compose.yml
  • A Phala Cloud account with an API key
  • A container registry account (Docker Hub, GHCR, or similar)

Secrets Management

Regardless of which CI platform you use, store your Phala Cloud API key as a secret. Never hardcode it in workflow files. GitHub: Go to Settings > Secrets and variables > Actions and add: GitLab: Go to Settings > CI/CD > Variables and add the same values as masked variables.
Rotate your API key immediately if it’s ever exposed in logs or committed to a repository. Generate a new one from Settings > API Keys in the Phala Cloud dashboard.

GitHub Actions with CLI

This is the simplest approach. The workflow builds your Docker image, pushes it to a registry, and deploys with phala deploy.
Your docker-compose.yml should reference the image variable:
The CLI detects existing CVMs by name. If my-tee-app already exists, it updates in place. Otherwise, it creates a new one.

GitHub Actions with JS SDK

If you need more control over the deployment (conditional logic, custom error handling, multi-step provisioning), use the SDK directly in a Node.js script.
1

Create a deploy script

Add scripts/deploy.mjs to your repository:
2

Create the workflow

GitLab CI

GitLab CI uses a similar pattern. Define a .gitlab-ci.yml in your repository root:
GitLab’s built-in container registry works well here. The $CI_REGISTRY_* variables are available automatically in every pipeline.

Terraform in CI

For infrastructure-as-code workflows, run Terraform in your pipeline. This is especially useful when you manage replicas, instance types, or environment variables declaratively.
Your infra/main.tf would look something like this:
Store your Terraform state in a remote backend (S3, GCS, or Terraform Cloud) so that CI runs can access the same state file. Without remote state, each pipeline run would try to create a new CVM instead of updating the existing one.

Verify Deployment

After your pipeline completes, confirm the CVM is running:

Troubleshooting

Authentication errors: Make sure PHALA_CLOUD_API_KEY is set correctly in your CI secrets. Test locally with phala status to verify the key works. Build failures: Ensure your Dockerfile builds locally with docker build . before pushing to CI. Deploy timeouts: If --wait times out, the CVM may still be starting. Check the dashboard or run phala cvms get to see the current status. Increase the timeout with --wait-timeout if your app takes longer to boot. Image pull errors: Verify the image tag in your compose file matches what was pushed to the registry. For private registries, set DSTACK_DOCKER_USERNAME and DSTACK_DOCKER_PASSWORD as encrypted secrets on the CVM.