Summary
When triggerType: tag is used, a single pushed tag triggers every application/compose in the repo that is configured to deploy on tag — there is no way to scope which service a given tag should deploy. For a monorepo where each service has its own tag prefix (very common with release-please + node-workspace, which emits per-package tags like web-v0.7.1, api-v0.5.1, shared-v0.5.1), this means a web-* release needlessly rebuilds and redeploys the API, and vice versa.
Feature request
Add an optional tag filter (glob/pattern) per application (and compose), analogous to the existing Watch Paths feature for branch pushes. When set, the service deploys on a tag only if the tag name matches the pattern; when empty, behavior is unchanged (any tag deploys — fully backward compatible).
Example config for a release-please monorepo:
| Service |
Tag filter |
| front |
web-v* |
| api |
api-v* |
| both (shared contract) |
additionally match shared-v* |
Why the current mechanisms don't cover this
- Watch Paths (
watchPaths + shouldDeploy with micromatch) already solves the equivalent problem for branch pushes — but it is only evaluated on the push-to-branch path of the webhook, not on the tag path.
- On the tag path,
apps/dokploy/pages/api/deploy/github.ts finds all apps with triggerType = "tag" for the repo and enqueues a deployment for each, in a for (const app of apps) loop, without ever comparing against tagName (the tag name is only used for the deployment title). So there is no hook where a per-service tag pattern could take effect today.
Suggested implementation (small, mirrors Watch Paths)
- Schema: add an optional
tagFilter: string | null (or tagFilters: string[]) column on application and compose (packages/server/src/db/schema/), plus a Drizzle migration.
- Filter helper: reuse
micromatch (already a dependency, used by watch-paths/should-deploy.ts). Something like:
export const matchesTag = (tagFilter: string | null | undefined, tagName: string): boolean => {
if (!tagFilter) return true; // empty = deploy on any tag (backward compatible)
return micromatch.isMatch(tagName, tagFilter);
};
- Webhook: in the tag branch of
github.ts, skip apps whose tagFilter doesn't match tagName before enqueuing — for both the applications and compose loops.
- UI: expose a "Tag Filter" input next to the existing "Watch Paths" field in the Git provider settings (
save-git-provider.tsx / save-git-provider-compose.tsx), shown when triggerType === "tag".
This keeps the tag path symmetric with the branch path (Watch Paths), is opt-in, and is fully backward compatible (empty filter = today's behavior).
Use case / impact
Monorepo with release-please (node-workspace plugin) emitting per-component tags. Today a single release that only touched the frontend still rebuilds the backend (a heavier build), doubling deploy time and resource use on the build node for no reason. A per-service tag filter lets each service deploy only on its own tag prefix, with shared-* fanning out to both when the shared contract changes.
Environment
- Dokploy
v0.29.13.
triggerType: tag, GitHub source, monorepo (pnpm workspace) with release-please per-package tags.
Summary
When
triggerType: tagis used, a single pushed tag triggers every application/compose in the repo that is configured to deploy on tag — there is no way to scope which service a given tag should deploy. For a monorepo where each service has its own tag prefix (very common withrelease-please+node-workspace, which emits per-package tags likeweb-v0.7.1,api-v0.5.1,shared-v0.5.1), this means aweb-*release needlessly rebuilds and redeploys the API, and vice versa.Feature request
Add an optional tag filter (glob/pattern) per application (and compose), analogous to the existing Watch Paths feature for branch pushes. When set, the service deploys on a tag only if the tag name matches the pattern; when empty, behavior is unchanged (any tag deploys — fully backward compatible).
Example config for a
release-pleasemonorepo:web-v*api-v*shared-v*Why the current mechanisms don't cover this
watchPaths+shouldDeploywithmicromatch) already solves the equivalent problem for branch pushes — but it is only evaluated on the push-to-branch path of the webhook, not on the tag path.apps/dokploy/pages/api/deploy/github.tsfinds all apps withtriggerType = "tag"for the repo and enqueues a deployment for each, in afor (const app of apps)loop, without ever comparing againsttagName(the tag name is only used for the deployment title). So there is no hook where a per-service tag pattern could take effect today.Suggested implementation (small, mirrors Watch Paths)
tagFilter: string | null(ortagFilters: string[]) column onapplicationandcompose(packages/server/src/db/schema/), plus a Drizzle migration.micromatch(already a dependency, used bywatch-paths/should-deploy.ts). Something like:github.ts, skip apps whosetagFilterdoesn't matchtagNamebefore enqueuing — for both theapplicationsandcomposeloops.save-git-provider.tsx/save-git-provider-compose.tsx), shown whentriggerType === "tag".This keeps the tag path symmetric with the branch path (Watch Paths), is opt-in, and is fully backward compatible (empty filter = today's behavior).
Use case / impact
Monorepo with
release-please(node-workspaceplugin) emitting per-component tags. Today a single release that only touched the frontend still rebuilds the backend (a heavier build), doubling deploy time and resource use on the build node for no reason. A per-service tag filter lets each service deploy only on its own tag prefix, withshared-*fanning out to both when the shared contract changes.Environment
v0.29.13.triggerType: tag, GitHub source, monorepo (pnpm workspace) withrelease-pleaseper-package tags.