If you prefer video content, check the YouTube tutorial here.

Prerequisites

  • Host the DNS of your domain on Cloudflare (other providers will be supported soon)
  • Have access to the Cloudflare account with API token

Create Cloudflare API Token

If you have not generated an API Token for your custom domain management then follow these steps:

Deployment

For more details, checkout the github repository for the dstack-ingress dstack examples.
For this deployment example, we will be an nginx application where the dstack-ingress container that will forward traffic the TARGET_ENDPOINT that points to the app service (running the nginx image) with an exposed port 80 . It is important to know that this can change based on how your docker app’s compose file is configured. Now on to the deployment. Go to you Phala Cloud Dashboard and deploy a new CVM. Select docker-compose.yml option for deployment then take the past the docker compose file below into the Advanced tab of the CVM configuration page.
services:
  dstack-ingress:
    image: kvin/dstack-ingress@sha256:2cc3bc50d71faa4d313084237b0f5d1d25963024f2484c7a6414aed075883cdd
    ports:
      - "443:443"
    environment:
      - DOMAIN=example.com
      - TARGET_ENDPOINT=http://app:80
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - GATEWAY_DOMAIN=_.${DSTACK_GATEWAY_DOMAIN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
    restart: unless-stopped
  app:
    image: nginx  # Replace with your application image
    restart: unless-stopped
volumes:
  cert-data:  # Persistent volume for certificates
Here’s an explanation of the configs:
  • DOMAIN: Your custom domain (i.e. your-domain.com ).
  • TARGET_ENDPOINT: Where the ingress should forward all incoming traffic — i.e. the upstream application service:port. In this case, we point to the nginx service app on port 80.
  • Other variables
    • CLOUDFLARE_API_TOKEN: Your Cloudflare API token
    • CERTBOT_EMAIL: Your email address used for Let’s Encrypt email notifications
    • SET_CAA: Leave it to true to enable CAA record setup. Necessary for a secure zt-https setup.
    • GATEWAY_DOMAIN: Leave it unchanged. It points to the dstack gateway domain automatically populated by Phala Cloud.
Now copy and paste the docker-compose.yaml code above into the compose.yml section similar to the screenshot below.
Next you need to grab your Cloudflare API Token for your domain, and fill in your encrypted secrets.
Click Deploy button and your CVM will deploy in a couple minutes with the custom domain. Here is an example of a custom domain deployed to phala.incipient.ltd.
Congratulations! You’ve successfully deployed your CVM with a custom domain. Your application is now secured with Zero Trust HTTPS, thanks to the seamless integration of Cloudflare DNS and Let’s Encrypt. If you are interested in the verification of this process check the #domain-attestation-and-verification.

Troubleshooting

First, make sure you have configured it properly. dstack-ingress is a sidecar in your docker compose file. When adding it, you should make sure:
  1. You have configured the necessary environmental variables and encrypted secrets according as described above
  2. Declare the cert-data volume in your docker compose file as it’s used by dstack-ingress
  3. dstack-ingress service is connected to the same network as the service specified in TARGET_ENDPOINT . e.g. If you set networks: [net1] for your app, you should also have it in dstack-ingress .
After deploying your app, you can find the dstack-ingress container logs. It should print logs like below:
2025-07-17T23:12:12.758817184Z Account registered.
2025-07-17T23:12:12.760403962Z Requesting a certificate for custom-test.phala.systems
2025-07-17T23:12:16.184509409Z Waiting 120 seconds for DNS changes to propagate
2025-07-17T23:14:22.000463333Z 
2025-07-17T23:14:22.000488847Z Successfully received certificate.
# ...
2025-07-17T23:14:22.085351978Z Generated evidences successfully
# ...
2025-07-17T23:14:29.396160169Z [Thu Jul 17 23:14:29 UTC 2025] Next renewal check in 12 hours
Finally, access the custom domain. You should see nginx access logs in the dstack-ingress container logs like below:
2025-07-17T23:16:15.748373090Z 10.0.3.1 - - [17/Jul/2025:23:16:15 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" "-"

Knowledge

Now you have the knowledge base on the custom domain deployment with a basic nginx dstack application. The features you have used to enable your custom domain are defined below:
  • Automatic SSL certificate provisioning and renewal via Let’s Encrypt
  • Cloudflare DNS configuration for CNAME, TXT, and CAA records
  • Nginx reverse proxy to route traffic to your application
  • Certificate evidence generation for verification

Domain Attestation and Verification

The dstack-ingress system provides mechanisms to verify and attest that your custom domain endpoint is secure and properly configured. This comprehensive verification approach ensures the integrity and authenticity of your application.

Evidence Collection

When certificates are issued or renewed, the system automatically generates a set of cryptographically linked evidence files:
  1. Access Evidence Files:
    • Evidence files are accessible at https://your-domain.com/evidences/
    • Key files include acme-account.json, cert.pem, sha256sum.txt, and quote.json
  2. Verification Chain:
    • quote.json contains a TDX quote with the SHA-256 digest of sha256sum.txt embedded in the report_data field
    • sha256sum.txt contains cryptographic checksums of both acme-account.json and cert.pem
    • When the TDX quote is verified, it cryptographically proves the integrity of the entire evidence chain
  3. Certificate Authentication:
    • acme-account.json contains the ACME account credentials used to request certificates
    • When combined with the CAA DNS record, this provides evidence that certificates can only be requested from within this specific TEE application
    • cert.pem is the Let’s Encrypt certificate currently serving your custom domain
You can check the example of the deployment at phala.incipient.ltd/evidences/.

CAA Record Verification

If you’ve enabled CAA records (SET_CAA=true), you can verify that only authorized Certificate Authorities can issue certificates for your domain:
dig CAA your-domain.com
The output will display CAA records that restrict certificate issuance exclusively to Let’s Encrypt with your specific account URI, providing an additional layer of security.

TLS Certificate Transparency

All Let’s Encrypt certificates are logged in public Certificate Transparency (CT) logs, enabling independent verification: CT Log Verification:
  • Visit crt.sh and search for your domain
  • Confirm that the certificates match those issued by the dstack-ingress system
  • This public logging ensures that all certificates are visible and can be monitored for unauthorized issuance