Custom Domain Setup for dstack Applications
This repository provides a solution for setting up custom domains with automatic SSL certificate management for dstack applications using various DNS providers and Let's Encrypt.
Overview
This project enables you to run dstack applications with your own custom domain, complete with:
- Automatic SSL certificate provisioning and renewal via Let's Encrypt
- Multi-provider DNS support (Cloudflare, Linode DNS, more to come)
- Automatic DNS configuration for CNAME, TXT, and CAA records
- Nginx reverse proxy to route traffic to your application
- Certificate evidence generation for verification
- Strong SSL/TLS configuration with modern cipher suites (AES-GCM and ChaCha20-Poly1305)
How It Works
The dstack-ingress system provides a seamless way to set up custom domains for dstack applications with automatic SSL certificate management. Here's how it works:
-
Initial Setup:
- When first deployed, the container automatically obtains SSL certificates from Let's Encrypt using DNS validation
- It configures your DNS provider by creating necessary CNAME, TXT, and optional CAA records
- Nginx is configured to use the obtained certificates and proxy requests to your application
-
DNS Configuration:
- A CNAME record is created to point your custom domain to the dstack gateway domain
- A TXT record is added with application identification information to help dstack-gateway to route traffic to your application
- If enabled, CAA records are set to restrict which Certificate Authorities can issue certificates for your domain
- The system automatically detects your DNS provider based on environment variables
-
Certificate Management:
- SSL certificates are automatically obtained during initial setup
- A simple background daemon checks for certificate renewal every 12 hours
- When certificates are renewed, Nginx is automatically reloaded to use the new certificates
- Uses a simple sleep loop instead of cron for reliability and easier debugging in containers
-
Evidence Generation:
- The system generates evidence files for verification purposes
- These include the ACME account information and certificate data
- Evidence files are accessible through a dedicated endpoint
Features
Multi-Domain Support (New!)
The dstack-ingress now supports multiple domains in a single container:
- Single Domain Mode (backward compatible): Use
DOMAINandTARGET_ENDPOINTenvironment variables - Multi-Domain Mode: Use
DOMAINSenvironment variable with custom nginx configurations in/etc/nginx/conf.d/ - Each domain gets its own SSL certificate
- Flexible nginx configuration per domain
Wildcard Domain Support
You can use a wildcard domain (e.g. *.myapp.com) to route all subdomains to a single dstack application:
- The TXT record is automatically set as
_dstack-app-address-wildcard.myapp.com(instead of_dstack-app-address.*.myapp.com) - CAA records use the
issuewildtag on the base domain - Requires dstack-gateway with wildcard TXT resolution support (dstack#545)
services: dstack-ingress: image: dstacktee/dstack-ingress:latest ports: - "443:443" environment: - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN} - DOMAIN=*.myapp.com - GATEWAY_DOMAIN=_.dstack-prod5.phala.network - CERTBOT_EMAIL=${CERTBOT_EMAIL} - SET_CAA=true - TARGET_ENDPOINT=http://app:80 volumes: - /var/run/dstack.sock:/var/run/dstack.sock - /var/run/tappd.sock:/var/run/tappd.sock - cert-data:/etc/letsencrypt restart: unless-stopped app: image: nginx restart: unless-stopped volumes: cert-data:
Usage
Prerequisites
- Host your domain on one of the supported DNS providers
- Have appropriate API credentials for your DNS provider (see DNS Provider Configuration for details)
Deployment
You can either build the ingress container and push it to docker hub, or use the prebuilt image at dstacktee/dstack-ingress:20250924.
Option 1: Use the Pre-built Image
The fastest way to get started is to use our pre-built image. Simply use the following docker-compose configuration:
services: dstack-ingress: image: dstacktee/dstack-ingress:20250929@sha256:2b47b3e538df0b3e7724255b89369194c8c83a7cfba64d2faf0115ad0a586458 ports: - "443:443" environment: # DNS Provider - DNS_PROVIDER=cloudflare # Cloudflare example - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN} # Common configuration - DOMAIN=${DOMAIN} - GATEWAY_DOMAIN=${GATEWAY_DOMAIN} - CERTBOT_EMAIL=${CERTBOT_EMAIL} - SET_CAA=true - TARGET_ENDPOINT=http://app:80 volumes: - /var/run/dstack.sock:/var/run/dstack.sock - /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
Multi-Domain Configuration
services: ingress: image: dstacktee/dstack-ingress:20250929@sha256:2b47b3e538df0b3e7724255b89369194c8c83a7cfba64d2faf0115ad0a586458 ports: - "443:443" environment: DNS_PROVIDER: cloudflare CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} CERTBOT_EMAIL: ${CERTBOT_EMAIL} GATEWAY_DOMAIN: _.dstack-prod5.phala.network SET_CAA: true DOMAINS: | ${APP_DOMAIN} ${API_DOMAIN} volumes: - /var/run/tappd.sock:/var/run/tappd.sock - letsencrypt:/etc/letsencrypt configs: - source: app_conf target: /etc/nginx/conf.d/app.conf mode: 0444 - source: api_conf target: /etc/nginx/conf.d/api.conf mode: 0444 restart: unless-stopped app-main: image: nginx restart: unless-stopped app-api: image: nginx restart: unless-stopped volumes: letsencrypt: configs: app_conf: content: | server { listen 443 ssl; server_name ${APP_DOMAIN}; ssl_certificate /etc/letsencrypt/live/${APP_DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${APP_DOMAIN}/privkey.pem; location / { proxy_pass http://app-main:80; } } api_conf: content: | server { listen 443 ssl; server_name ${API_DOMAIN}; ssl_certificate /etc/letsencrypt/live/${API_DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${API_DOMAIN}/privkey.pem; location / { proxy_pass http://app-api:80; } }
Core Environment Variables:
DNS_PROVIDER: DNS provider to use (cloudflare, linode)DOMAIN: Your custom domain (for single domain mode)DOMAINS: Multiple domains, one per line (supports environment variable substitution like${APP_DOMAIN})GATEWAY_DOMAIN: The dstack gateway domain (e.g._.dstack-prod5.phala.networkfor Phala Cloud)CERTBOT_EMAIL: Your email address used in Let's Encrypt certificate requestsTARGET_ENDPOINT: The plain HTTP endpoint of your dstack application (for single domain mode)SET_CAA: Set totrueto enable CAA record setupCLIENT_MAX_BODY_SIZE: Optional value for nginxclient_max_body_size(numeric with optionalk|m|gsuffix, e.g.50m) in single-domain modePROXY_READ_TIMEOUT: Optional value for nginxproxy_read_timeout(numeric with optionals|m|hsuffix, e.g.30s) in single-domain modePROXY_SEND_TIMEOUT: Optional value for nginxproxy_send_timeout(numeric with optionals|m|hsuffix, e.g.30s) in single-domain modePROXY_CONNECT_TIMEOUT: Optional value for nginxproxy_connect_timeout(numeric with optionals|m|hsuffix, e.g.10s) in single-domain modePROXY_BUFFER_SIZE: Optional value for nginxproxy_buffer_size(numeric with optionalk|msuffix, e.g.128k) in single-domain modePROXY_BUFFERS: Optional value for nginxproxy_buffers(format:number size, e.g.4 256k) in single-domain modePROXY_BUSY_BUFFERS_SIZE: Optional value for nginxproxy_busy_buffers_size(numeric with optionalk|msuffix, e.g.256k) in single-domain modeCERTBOT_STAGING: Optional; set this value to the stringtrueto set the--stagingserver option on thecertbotcli
Backward Compatibility:
- If both
DOMAINandTARGET_ENDPOINTare set, the system operates in single-domain mode with auto-generated nginx config - If
DOMAINSis set, the system operates in multi-domain mode and expects custom nginx configs in/etc/nginx/conf.d/ - You can use both modes simultaneously
For provider-specific configuration details, see DNS Provider Configuration.
Option 2: Build Your Own Image
If you prefer to build the image yourself:
- Clone this repository
- Build the Docker image using the provided build script:
./build-image.sh yourusername/dstack-ingress:tag
Important: You must use the build-image.sh script to build the image. This script ensures reproducible builds with:
- Specific buildkit version (v0.20.2)
- Deterministic timestamps (
SOURCE_DATE_EPOCH=0) - Package pinning for consistency
- Git revision tracking
Direct docker build commands will not work properly due to the specialized build requirements.
- Push to your registry (optional):
docker push yourusername/dstack-ingress:tag
- Update the docker-compose.yaml file with your image name and deploy
gRPC Support
If your dstack application uses gRPC, you can set TARGET_ENDPOINT to grpc://app:50051.
example:
services: dstack-ingress: image: dstacktee/dstack-ingress:20250929@sha256:2b47b3e538df0b3e7724255b89369194c8c83a7cfba64d2faf0115ad0a586458 ports: - "443:443" environment: - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN} - DOMAIN=${DOMAIN} - GATEWAY_DOMAIN=${GATEWAY_DOMAIN} - CERTBOT_EMAIL=${CERTBOT_EMAIL} - SET_CAA=true - TARGET_ENDPOINT=grpc://app:50051 volumes: - /var/run/dstack.sock:/var/run/dstack.sock - /var/run/tappd.sock:/var/run/tappd.sock - cert-data:/etc/letsencrypt restart: unless-stopped app: image: your-grpc-app restart: unless-stopped volumes: cert-data:
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:
-
Access Evidence Files:
- Evidence files are accessible at
https://your-domain.com/evidences/ - Key files include
acme-account.json,cert.pem,sha256sum.txt, andquote.json
- Evidence files are accessible at
-
Verification Chain:
quote.jsoncontains a TDX quote with the SHA-256 digest ofsha256sum.txtembedded in the report_data fieldsha256sum.txtcontains cryptographic checksums of bothacme-account.jsonandcert.pem- When the TDX quote is verified, it cryptographically proves the integrity of the entire evidence chain
-
Certificate Authentication:
acme-account.jsoncontains 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.pemis the Let's Encrypt certificate currently serving your custom domain
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:
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
License
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.