Skip to main content
This guide shows you how to use your own domain (like api.mycompany.com) instead of the default Phala Cloud domain.

Prerequisites

  • A domain with DNS hosted on Cloudflare (Linode and Namecheap also supported)
  • Access to your DNS provider’s API token
  • A deployed CVM application

Step 1: Create DNS API Token

  1. Go to your Cloudflare Dashboard
  2. Select your domain
  3. Go to Get Your API Token
  4. Click Create Token
  5. Use the Edit zone DNS template
  6. Select your domain in Zone Resources
  7. Click Continue to summary and create the token
  8. Copy the token for later use

Step 2: Add dstack-ingress to Your Deployment

Update your docker-compose.yml to include the dstack-ingress container:
Set DOMAIN to your custom domain and TARGET_ENDPOINT to your app’s internal endpoint. TARGET_ENDPOINT tells dstack-ingress where to forward incoming traffic:
  • app:80 - Forward to service named “app” on port 80
  • api:3000 - Forward to service named “api” on port 3000
  • http://app:80 and grpc://app:50051 are also accepted, but the protocol prefix is stripped before proxying
  • Use the service name from docker-compose, not localhost
dstack-ingress is a HAProxy-based L4/TCP proxy. It terminates TLS and forwards the TCP stream to TARGET_ENDPOINT; it does not apply Nginx-style HTTP redirects, header rules, body limits, or buffering settings. Put HTTP-specific behavior in your application or in a separate HTTP proxy behind TARGET_ENDPOINT if needed.

Step 3: Configure Environment Variables

In the Phala Cloud dashboard, add these encrypted secrets:
  • CLOUDFLARE_API_TOKEN: Your API token from Step 1
  • CERTBOT_EMAIL: Your email for Let’s Encrypt notifications

Step 4: Deploy

Deploy your updated docker-compose.yml through the dashboard. The first deployment typically takes 3–5 minutes as it:
  1. Configures DNS records
  2. Requests SSL certificate from Let’s Encrypt
  3. Starts the HAProxy TCP proxy

Step 5: Verify

After deployment, you can:
  1. Access your domain: Visit https://api.mycompany.com
  2. Check certificate: Your browser should show a valid Let’s Encrypt certificate
  3. Verify evidence: Visit https://api.mycompany.com/evidences/ to see attestation data

Multiple Custom Domains

You can route multiple custom domains from one ingress container by using DOMAINS and ROUTING_MAP:

Why We Pin Infrastructure Images

The dstack-ingress image uses a SHA256 hash instead of a tag to ensure:
  • Auditability: Anyone can verify the exact infrastructure code handling domains
  • Immutability: The ingress behavior never changes unexpectedly
  • Security: Prevents infrastructure image substitution
  • Reproducibility: Domain handling is consistent across all deployments

Security Features

When SET_CAA=true is enabled:
  • CAA records restrict certificate issuance to the TEE managed Let’s Encrypt account
  • Only certificates requested from within your TEE can be issued
  • Certificate transparency logs can be monitored for unauthorized certificates
Cloudflare Users: Cloudflare automatically overrides CAA records at the apex domain (e.g., example.com). This prevents domain attestation from working correctly.Solution options:
  1. Use a subdomain (recommended): Deploy to app.example.com instead of example.com
  2. Disable Cloudflare’s CAA management: See Cloudflare CAA documentation
To verify your CAA record is working:
Should return the Let’s Encrypt account URI with validationmethods=dns-01;accounturi=...

Troubleshooting

Check Logs

Look for dstack-ingress container logs in the dashboard:

Certbot Error: “too many certificates (5) already issued for this exact set of identifiers”

You may have forgotten to attach the data volume.

Other Common Issues

DNS not propagating: Wait 2-5 minutes for DNS changes to propagate globally. Certificate request failed: Verify your API token has permission to edit DNS records. Connection refused: Ensure TARGET_ENDPOINT points to the correct service:port. CAA record conflicts: Some providers (like Linode) don’t allow CAA and CNAME on the same subdomain. The system will automatically use A records instead.

Next Steps