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.
GitHub Actions with CLI
This is the simplest approach. The workflow builds your Docker image, pushes it to a registry, and deploys withphala deploy.
docker-compose.yml should reference the image variable:
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.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.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 surePHALA_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.
