What problem will this feature address?
Preview Deployments currently generate subdomains using a fixed pattern that looks like preview-{appName}-{appHash}-{prHash}.{wildcardDomain}.
For example:
https://preview-my-app-a1b2c3-x9y8z7.example.com/
https://preview-my-app-a1b2c3-p4q5r6.example.com/
The two random hashes at the end make it impossible to tell at a glance which PR a preview belongs to. For teams reviewing multiple PRs at once, it would be useful to have preview URLs that encode meaningful context (PR number, branch name, etc.) so reviewers can identify previews at a glance and share links in PR comments.
Describe the solution you'd like
Allow users to define a template for the preview subdomain using variables, for example:
- ${prNumber} — the pull request number (e.g. 123)
- ${branchName} — a slugified branch name (e.g. feature-login)
- ${appName} — the application name (e.g. my-app)
- ${uniqueId} — the existing random hash
So a user could configure a template like:
pr-${prNumber}.example.com
and get pr-123.example.com for PR #123, instead of preview-my-app-a1b2c3-x9y8z7.example.com
I do not know how coolify does it but I know what I want here is possible there.
Describe alternatives you've considered
Manually assigning a domain to each preview after deployment — defeats the purpose of automatic preview deployments. And after rename, the rebuild needs to be triggered.
Additional context
I made an agent search through the codebase to validate that feature is not supported. The following analysis was made by AI:
I traced the full flow end-to-end to confirm this isn't already possible:
Subdomain generation — In packages/server/src/services/preview-deployment.ts the subdomain is hardcoded as:
const appName = `preview-${application.appName}-${generatePassword(6)}`;
Wildcard replacement — generateWildcardDomain throws unless previewWildcard starts with *., then does a plain baseDomain.replace("*", hash). No ${...} variable interpolation.
Downstream — createDomain just .trim()s the host and inserts it; manageDomain reads domain.host and writes it verbatim into the Traefik router rule (only transform is toPunycode, a no-op for ASCII). Nothing mutates the host after it's written.
UI + validation — The settings form validates wildcardDomain as a plain z.string(), and db/validations/domain.ts only checks for non-empty/no-whitespace. No template parsing anywhere.
Will you send a PR to implement it?
Yes
What problem will this feature address?
Preview Deployments currently generate subdomains using a fixed pattern that looks like preview-{appName}-{appHash}-{prHash}.{wildcardDomain}.
For example:
https://preview-my-app-a1b2c3-x9y8z7.example.com/
https://preview-my-app-a1b2c3-p4q5r6.example.com/
The two random hashes at the end make it impossible to tell at a glance which PR a preview belongs to. For teams reviewing multiple PRs at once, it would be useful to have preview URLs that encode meaningful context (PR number, branch name, etc.) so reviewers can identify previews at a glance and share links in PR comments.
Describe the solution you'd like
Allow users to define a template for the preview subdomain using variables, for example:
So a user could configure a template like:
pr-${prNumber}.example.com
and get pr-123.example.com for PR #123, instead of preview-my-app-a1b2c3-x9y8z7.example.com
I do not know how coolify does it but I know what I want here is possible there.
Describe alternatives you've considered
Manually assigning a domain to each preview after deployment — defeats the purpose of automatic preview deployments. And after rename, the rebuild needs to be triggered.
Additional context
I made an agent search through the codebase to validate that feature is not supported. The following analysis was made by AI:
I traced the full flow end-to-end to confirm this isn't already possible:
Subdomain generation — In
packages/server/src/services/preview-deployment.tsthe subdomain is hardcoded as:Wildcard replacement —
generateWildcardDomainthrows unlesspreviewWildcardstarts with*., then does a plainbaseDomain.replace("*", hash). No${...}variable interpolation.Downstream —
createDomainjust.trim()s the host and inserts it;manageDomainreadsdomain.hostand writes it verbatim into the Traefik router rule (only transform istoPunycode, a no-op for ASCII). Nothing mutates the host after it's written.UI + validation — The settings form validates
wildcardDomainas a plainz.string(), anddb/validations/domain.tsonly checks for non-empty/no-whitespace. No template parsing anywhere.Will you send a PR to implement it?
Yes