Changelog
All notable changes to this project will be documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.1.0] - 2026-09-08
Added
app_name_prefixondokploy_compose,dokploy_application,dokploy_postgres,dokploy_mysql,dokploy_mariadb,dokploy_mongo,dokploy_redis, anddokploy_libsql(#41, the feature that #39 asked for). Dokploy builds every app name as<prefix>-<six random characters>at create, and the UI lets you type the prefix apart from the display name. The attribute gives the same control:name = "glitchtip"withapp_name_prefix = "glitchtip-app03-dfw"stores the app nameglitchtip-app03-dfw-<suffix>, which hostnames and connection strings use. It defaults toname. Use lowercase letters, digits, dots, underscores, and hyphens, 56 characters at most; the server lowercases the seed, so this rule keeps the configured value equal to the stored one. A change replaces the resource, because Dokploy fixes the app name at create. The provider derives the state value fromapp_name, so an imported resource shows no diff when the configuration matches the stored app name.- A Changelog page in the registry docs, under Guides.
make docsgenerates it from this file.
Changed
- The
app_namedescription and the plan-time error for a configuredapp_namepoint atapp_name_prefix.
[1.0.1] - 2026-09-08
Fixed
app_nameondokploy_compose,dokploy_application, and the six database engines (#39). Dokploy appends a random suffix to any app name it receives on create and ignores the field on update, so a configured value could never match the stored one. The apply failed with “Provider produced inconsistent result after apply” and left the resource tainted. The provider now rejects a configuredapp_nameat plan time with a message that says why. The attribute keeps its shape. When you omit it, the create request seeds the server withname, so a generated app name reads<name>-<suffix>instead of a random phrase, asdokploy_libsqlalready did. A resource that the failure left tainted: removeapp_namefrom the configuration, then runterraform untaint <address>; the next plan is empty.
[1.0.0] - 2026-09-05
The first stable release. It has no configuration change: every attribute from v0.13.0 keeps its shape, and a v0.13.0 state loads with an empty plan. From this release the provider follows semantic versioning. The Stability section of the README and of the provider index states the rules.
Changed
- The Go toolchain moves to 1.26.6, and grpc, x/net, x/text, and x/crypto
move to their current versions.
govulncheckreported six reachable vulnerabilities on the v0.13.0 build: four in the Go 1.26.5 standard library, one in grpc, and one in x/net and the standard library. It reports none now. Thevulnjob intest.ymlandmake vulnrun it on every pull request. - Every resource and data source page on the registry carries a subcategory. The sidebar groups the 46 resources and the 19 data sources into twelve sections instead of one list.
- Compatibility, checked on 2026-09-05: the project and destination packages of the acceptance suite pass on Terraform 1.5.7 and on OpenTofu 1.12.6 with the provider binary from this repository. CI runs the suite on Terraform 1.16.1. The provider is not on the OpenTofu registry yet.
dogfood/generate_imports.pyenumerates the v0.13 resources: SSH keys, servers, registries, certificates, GitLab, Bitbucket, and Gitea providers, notifications, AI settings, organizations, and the permissions of each member. It lists a user as a comment, because Dokploy never returns a password.- Upgrade tests for
dokploy_projectanddokploy_application: v0.13.0 from the registry creates the record, and the local build plans no change. Unit tests cover the flatten and lookup functions of the gitlab, gitea, certificate, ai, and destination resources and of the organization, user, and git-provider data sources. - Repository files:
SECURITY.md, an issue template for bugs and one for features, a pull request template with the touchpoint list, and a Dependabot configuration for the Go modules and the actions.
Fixed
dokploy_application: theregistry_iddescription no longer says that the provider has no registry resource.dokploy_registryexists since v0.13.0.dokploy_environment_variables: theenvironment_iddescription is one sentence again.
[0.13.0] - 2026-09-05
The coverage release before v1.0.0. It has no breaking change: every attribute from v0.12.0 keeps its shape, and a v0.12.0 state loads with an empty plan. It closes the gaps between this provider and the other Dokploy providers on the registry.
Added
- Servers and keys:
dokploy_ssh_keyanddokploy_server, each with a data source. The server resource stores the record; the setup that installs Docker on the machine stays a step in the Dokploy UI. dokploy_registry(a container registry login; Dokploy runsdocker loginon create and update),dokploy_certificate(a TLS certificate for Traefik), anddokploy_ai(an OpenAI-compatible endpoint for the AI features).- Git providers:
dokploy_gitlab_provider,dokploy_bitbucket_provider, anddokploy_gitea_provider, each with a data source that reportsis_configured.dokploy_applicationanddokploy_composegain thegitlab,bitbucket, andgiteasource blocks. - Twelve notification resources, one per channel:
dokploy_slack_notification,dokploy_discord_notification,dokploy_telegram_notification,dokploy_email_notification,dokploy_resend_notification,dokploy_gotify_notification,dokploy_ntfy_notification,dokploy_mattermost_notification,dokploy_lark_notification,dokploy_teams_notification,dokploy_pushover_notification, anddokploy_custom_notification. They share the eight event flags. - Organization and access:
dokploy_organization(with a data source that also returns the active organization),dokploy_user(an account with an initial password and a member role; with a data source),dokploy_user_permissions(the full permission set of a member; destroy resets it), anddokploy_api_key(the key is kept as a sensitive value; every input replaces the key; no import). dokploy_environment_variables: a map that owns theenvtext of an application, a compose, or an environment.- Every new secret has a write-only companion (
<name>_woand<name>_wo_version), listed in the secrets guide. - The Usage examples guide, with six complete configurations.
Changed
- The client recovers the id of a record whose create endpoint returns no body (SSH keys, notifications, AI settings, GitLab and Bitbucket providers) from a list diff, and tells a concurrent sibling apart by the request fields instead of failing.
- The acceptance suite publishes a
registry:2container on the Docker daemon’s loopback for the registry tests, and generates SSH key pairs throughsshKey.generate.
Known limits
- A GitLab or Gitea connection needs one authorization in the Dokploy UI before a deploy can clone from it.
dokploy_usercannot change a password: Dokploy has no reset endpoint for another user, so a password change replaces the account.dogfood/generate_imports.pydoes not enumerate the new resources.
[0.12.0] - 2026-09-05
The hardening release before v1.0.0. It has no breaking change: every attribute from v0.11.0 keeps its shape, and a v0.11.0 state loads with an empty plan. The acceptance suite proves that with the v0.11.0 provider from the registry, on every resource that gains an attribute here.
Added
-
Write-only secrets. Every sensitive attribute that a resource sends to the server has a write-only companion:
<name>_wo(write-only, sensitive) and<name>_wo_version(a number). Terraform keeps a write-only value out of the plan and the state, and the provider keeps the server’s value out of the state on every refresh. A new value reaches the server when<name>_wo_versionchanges. The pairs:database_password_woon the six database engines, anddatabase_root_password_woondokploy_mysqlanddokploy_mariadb.access_key_woandsecret_access_key_woondokploy_destination.password_woondokploy_security.- The secret of each
dokploy_vault_providerconfig block:hashicorp.token_wo,infisical.client_secret_wo,aws.access_key_id_wo,aws.secret_access_key_wo,doppler.service_token_wo,azure.client_secret_wo, andscaleway.secret_key_wo. The Dokploy API masks these secrets on read, so the provider sends the companion’s value on every update, and the version only starts an update when nothing else changed.
The plain attributes stay.
database_password,access_key,secret_access_key,password, and the vault secrets turn Optional; a validator still demands exactly one of the plain attribute and its companion. A write-only value needs Terraform 1.11 or later. A configuration without the companions works on Terraform 1.5 as before. See the secrets guide. -
dogfood/generate_imports.pyenumeratesdokploy_compose(with its domains, mounts, and backups) anddokploy_network, lists vault providers as comments, and skips a compose without a usable source.
Changed
- The Get started guide states the two API key shapes. A key from the UI
with “Enable Rate Limiting” off, its default, has no limit. A key from a
raw
user.createApiKeycall gets 10 requests per 24 hours, then401. dokploy_libsql: on Dokploy v0.30.5 a replica needs thecommandoverride to replicate. Dokploy stores the role and the primary URL and passes them to the container, but startssqldwith a fixed command that reads neither. The resource note, thesqld_node,sqld_primary_url, andcommanddescriptions, the example, and the README say so.- The README and the provider index state the support policy: the acceptance suite runs on the latest Dokploy release, and the provider targets the pinned version. Older servers are untested.
- CI: the acceptance workflow installs one pinned Terraform CLI (1.16.1)
instead of one download per test package, and pulls the seven suite
images before the tests start.
acceptance/up.shforwardsDOKPLOY_VERSION, so a rig at the pinned or an older Dokploy version can be built.
Fixed
- The eight
*.deploycalls get a 10-minute deadline. The old 60-second client timeout failed adokploy_libsqlcreate while the server finished the deploy.
[0.11.0] - 2026-09-05
The last minor release with breaking changes before v1.0.0. The upgrade guide shows the old and the new configuration for each breaking change.
Breaking
dokploy_backup: thescheduleattribute is nowcron_expression, the name thatdokploy_scheduleanddokploy_volume_backupalready use. The Dokploy wire field is unchanged. The schema version moves to 1, and a state upgrader moves the value to the new name, so a state from v0.10.4 loads without an error. Rename the attribute in your configuration; a configuration that still setsschedulefails at plan time with an unsupported-argument error. The acceptance suite proves the upgrade: it creates the backup with v0.10.4 from the registry, then plans with the local build and the new name, and the plan is empty.- The
dokploy_libsqldata source no longer exposesdatabase_password. The five engine data sources never did; one convention now covers all six.database_userstays. A configuration that references the attribute fails at plan time with an unsupported-attribute error. Read the password from thedokploy_libsqlresource instead. dokploy_composeno longer hasisolated_deploymentandisolated_deployments_volume. Dokploy deprecated Isolated Deployment in v0.30.0;service_networksreplaces it. The provider no longer sendsisolatedDeploymentandisolatedDeploymentsVolumetocompose.update, and the server keeps the stored values (dialect B, verified live on v0.30.5). The schema version moves to 1, and a state upgrader drops the two attributes from a v0.10.4 state, so the state loads and the plan is empty. The acceptance suite proves it against v0.10.4 from the registry. Remove the two attributes from your configuration.
Added
dokploy_projectand thedokploy_projectdata source exposeproduction_environment_id: the id of the environment that carries the server’sisDefaultflag. Dokploy creates that environment with the project and names itproduction. The provider selects it with the flag, not with the name, so a rename of the environment does not change the value. When no environment carries the flag, the attribute is null and the provider adds a warning. Use it in place of the[for e in dokploy_project.example.environments : e.id if e.name == "production"][0]expression. On the resource, the attribute keeps its prior value in the plan of a project update. That matters:environmentsis unknown in the plan of every project update, so the for-expression also becomes unknown, and an unknownenvironment_idforces a replacement of the service that references it. Live check on the rig (Dokploy v0.30.5): a change to the project description planned a destroy and re-create of an application that used the for-expression. Withproduction_environment_idthe same change planned an in-place project update only. Move everyenvironment_idto the new attribute.
Changed
- CI: the test and acceptance workflows pin
actions/checkoutv7.0.1 andactions/setup-gov7.0.0, which run on Node.js 24. GitHub printed a Node.js 20 deprecation notice on every run with the v4 and v5 pins. The release workflow moves its pins in its own change. - Every example and guide uses
dokploy_project.example.production_environment_idin place of the[for e in ... : e.id if e.name == "production"][0]expression, and theenvironment_iddescriptions ofdokploy_application,dokploy_compose,dokploy_libsqland the five engine resources point at the new attribute. The Get started guide explains when theenvironmentslist is still the right tool, and why aforexpression over it forces a replacement of the service on every project update.
[0.10.4] - 2026-09-04
Changed
- The documentation now follows ASD-STE100 Simplified Technical English: active voice, simple tenses, short sentences, one instruction per sentence, and consistent terms. This covers the README, CONTRIBUTING, the provider index page, the four guides, the dogfood README, every resource and data source description that renders into the registry pages, and the comments in the examples. The examples also use neutral names in place of the maintainer’s own project names. The guide titles change to “Get started” and “Adopt an existing Dokploy server”; the page paths and anchors are unchanged. No schema, default, or behavior changes.
[0.10.3] - 2026-09-04
Changed
- The stated compatibility pin moves from Dokploy v0.30.3 to v0.30.5
(README and the provider index page). The endpoint census snapshot
now comes from a fresh v0.30.5 install (2026-09-04). The upstream
v0.30.3…v0.30.5 diff carries four request-schema changes, none on
a request struct this provider transmits:
compose.deployandcompose.redeployaccept an optionalfreshVolumesflag (a one-shotdocker compose down --volumesbefore the deploy, which this provider never sends),application.deployNginxQuickstartis a new onboarding mutation, thednsProviderrecord endpoints acceptproxiedand nine record types, and the Gotify and ntfy notification endpoints acceptserverThreshold. Two changes sit below the request schema and were probed live:vaultProvidergains a seventh type,phase(Phase.dev), whichdokploy_vault_providerdoes not model yet; and every database engine’s deploy now waits up to 45 seconds for the swarm service to converge, so a container that never starts fails the deploy call with adid not convergeerror and statuserrorinstead of a falsedone. The acceptance suite for this release ran against a fresh v0.30.5 install.
Fixed
- Documentation sweep.
dokploy_application: theenvandbuild.typedescriptions no longer claim thatbuild_secrets,heroku_versionandrailpack_versionare unexposed; all three have been schema attributes since v0.4.0.dokploy_compose: the import example names a label the example configuration defines, andtrigger_typelists its two values.dokploy_mount, the adopting guide and the dogfood README: the auto-created data mount note now coversdokploy_libsql(live-verified:<appName>-dataat/var/lib/sqld).dokploy_vault_provider: registry descriptions no longer cite internal development files, and the resource description names the unmodeledphasetype. README, the provider index page and the four guides: the version constraint examples move from~> 0.6to~> 0.10; “five engines” becomes six wherever LibSQL is included; the index page lists networks and vault providers; the README’s coverage gaps add DNS providers and the Phase vault type and name every by-name data source; the adopting guide and the dogfood README state thatgenerate_imports.pydoes not enumerate compose services, networks or vault providers; the deploy guide records the v0.30.5 convergence wait, thefreshVolumesflag, and that the MariaDB and MongoDB default images are still missing on v0.30.5.
[0.10.2] - 2026-09-01
Changed
- The stated compatibility pin moves from Dokploy v0.30.2 to v0.30.3
(README and the provider index page). The endpoint census snapshot
now comes from a fresh v0.30.3 install (2026-09-01). The upstream
v0.30.2…v0.30.3 diff carries two request-schema changes, both on
endpoints this provider does not call:
domain.validateDomainreplacesserverIpwithserverId, andnetwork.resyncis a new operational mutation. The network read shape gains a nullabledockerIdfield on all three read paths (live-probed); this client leaves it unmodeled and decodes past it. The OpenAPI route now rejects oversized request bodies with HTTP 413; every payload this provider sends is far below the limit. No request struct this provider transmits changed. The acceptance suite for this release ran against a fresh v0.30.3 install.
[0.10.1] - 2026-08-30
Changed
- The stated compatibility pin moves from Dokploy v0.30.0 to v0.30.2
(README and the provider index page). The endpoint census snapshot
was already taken against v0.30.2 (2026-08-19). The upstream
v0.30.0…v0.30.2 diff was re-read for this release and carries no
request-schema changes:
project.allnarrows its response columns for restricted members, the GitHub webhook applies its preview limit only to new pull requests, and stack deploys stop quoting.envvalues. None of these touch a request struct this provider transmits. The acceptance suite for this release ran against a fresh v0.30.2 install.
[0.10.0] - 2026-08-22
Added
-
dokploy_vault_providerresource: create, update, and destroy a secret-vault connection Dokploy can pull runtime secrets from at deploy time, one of six provider types -hashicorp(also covers OpenBao),infisical,aws,doppler,azure,scaleway- each its own typed, mutually exclusive config block.assignmentslinks the vault provider to projects and, optionally, specific environments within them; an empty list is legal.verify_connectionis opt-in (defaultfalse) and, when set, callsvaultProvider.testConnectionagainst the real vault before Create or Update writes anything, so a bad credential or an unreachable server fails the apply instead of creating a broken vault provider.Dokploy masks every secret field as the literal string
********on every read - create, read, and update alike - so this provider cannot detect a config value changed in the Dokploy UI, andterraform importcannot recover a config block; the first apply after import re-writes it in full from configuration. A server-side defect independent of this -vaultProvider.create’s duplicate-name rejection is a raw HTTP 500 that leaks the failed request’s secrets in cleartext, observed on doppler and hashicorp - is guarded on two sides: a best-effort name-uniqueness pre-check runs before any secret reaches the server, and every server error text reaching a diagnostic in Create, Update, and theverify_connectioncheck is scrubbed of every configured secret value first - Read and Delete carry no secrets. A scrubbed secret in an error message reads as(redacted).
Notes
- This closes the wave-6 slate.
vaultProvider.listSecretNamesand alldnsProviderendpoints stay unmodeled by decision:listSecretNamesis read-only UI surface with no Terraform-shaped use, and DNS belongs to the officialcloudflare/awsproviders, not this one.
[0.9.0] - 2026-08-20
Added
-
dokploy_networkresource: create and destroy Docker networks (bridge or overlay) withinternal,attachable,enable_ipv4/enable_ipv6,mtu, andipamaddress pools. Networks are immutable - Dokploy has nonetwork.updateendpoint - so every attribute isRequiresReplaceand changing any of them replaces the network.network.removesucceeds even while a network is still attached to an application; the reference is left dangling until that application is next updated or redeployed. -
dokploy_networkdata source: look up a network by id or name, for networks created or imported in the Dokploy UI.ipamis not exposed - a consumer needs only the id to attach a service to the network.
Notes
network.import,network.recreate, andnetwork.networksToSyncstay unmodeled. They are UI conveniences:network.importadopts a Docker-level network into Dokploy’s database, which a Terraform resource cannot address until it has a Dokploy id, and Terraform already expresses a rebuild through replace or taint without needingnetwork.recreate. Replace/taint and thedokploy_networkdata source cover both - see the resource docs for the import workaround.- The
dnsProviderendpoints stay unmodeled by decision: DNS belongs to the officialcloudflare/awsproviders, not this one.vaultProviderlands asdokploy_vault_providerin v0.10.0 (wave 6c).
[0.8.0] - 2026-08-19
Added
-
Dokploy v0.30.0 support. The census pin for endpoint fields moves from v0.29.13 to v0.30.0. Wave 6a probed the acceptance rig at v0.30.2, the installer’s current v0.30.x build at the time, and the census snapshot reflects that probe.
docs/index.mdnow states the v0.30.0 pin. Newer releases stay untested until the acceptance suite exercises them. -
network_idsanddetach_dokploy_networkondokploy_application, the five database engines (dokploy_postgres,dokploy_mysql,dokploy_mariadb,dokploy_mongo,dokploy_redis), anddokploy_libsql. These attributes attach a service to extra Docker networks beyond the defaultdokploy-network, or detach that default network. A network attachment change applies on the next deploy, not on apply - the same ruleenvandbuild_secretsalready follow.network_idsrejects an empty set at plan time; omit the attribute instead.Verified live against v0.30.2, 2026-08-19: an explicit
nullrequest clears the field to a storednull, never back to the fresh-create default of[]. The read path maps both shapes to the same Terraform value, or every plan after a clear would show a spurious diff. -
service_networks,create_env_file, andiconondokploy_compose.service_networksis compose’s per-service form ofnetwork_ids: each entry names one compose service and the network ids to attach to it, with its owndetach_dokploy_networktoggle, and it applies on the next deploy too.create_env_filewrites the environment variables to a.envfile for the compose project; it defaults totrue, the same as the server’s fresh-create default.iconsets the service icon shown in the Dokploy UI, as an icon name or a data URI up to 2 MB.create_env_fileon compose does not follow dialect A:compose.saveEnvironmentandcompose.updatekeep the stored value when the request omits the key.application.saveEnvironmentreturns an HTTP 400 on the same omission. -
enabledondokploy_domain.falseremoves the domain’s route from Traefik but keeps its configuration, so a later apply can re-enable the domain without new certificates or paths.enableddefaults totrue, the server’s own default on a bare create.domain.createcannot expressenabled = falsein one call - the field exists only ondomain.update. The resource creates the domain enabled first, then disables it in a second call when the configuration setsenabled = false.
Deprecated
isolated_deploymentandisolated_deployments_volumeondokploy_compose. Dokploy deprecates Isolated Deployment upstream in v0.30.0; useservice_networksinstead. Both attributes still work - Dokploy still accepts and stores them - and this provider keeps them until Dokploy removes them upstream.
Notes
- Dokploy v0.30.0 introduces new
network,dnsProvider, andvaultProviderrouters. These are not resources in this release. They land as resources in v0.9.0 and v0.10.0 (waves 6b and 6c).
[0.7.0] - 2026-08-12
Added
-
dokploy_libsqlresource and data source: a Dokploy LibSQL (sqld) database service - a distributed SQLite database.It sits outside the shared
database.Kindabstraction the other five engines use, even though Dokploy treats it as a database engine in every other sense - it hasdatabase_user/database_passwordand its ownsaveEnvironmentendpoint. Three server behaviours break the shared abstraction:libsql.createreturns the literaltruerather than the created record, so the resource has to locate it itself the same waydokploy_backupdoes;libsql.saveExternalPortscarries three ports (external_port,external_admin_port,external_grpc_port) whereKind.SaveExternalPortonly models one; andsqld_node,sqld_primary_urlandenable_namespacesare fields none of the five engines have, one of them a bool, whichKind.CredentialAttrscannot express.Clearing all three external ports at once takes two
saveExternalPortscalls, not one: the server 400s a single request that nulls all three ports together (“Either externalPort, externalGRPCPort or externalAdminPort must be provided”), so a full clear is split two-then-one.Three cross-field rules are enforced at plan time, not left for the server to reject at apply:
sqld_node = "replica"requiressqld_primary_url; a non-replica - including the default,"primary"- must NOT setsqld_primary_url; and a replica cannot set any of the three external ports at all, since Dokploy rejects everysaveExternalPortscall whilesqld_nodeisreplica, regardless of which ports the request carries. A transition fromprimaryintoreplicaclears the external ports before flippingsqld_node, not after -libsql.updateaccepts the flip while ports are still set server-side, and flipping first would leave those ports permanently stuck, since a replica rejects the very call that would clear them.app_nameis Computed-only, the only attribute in this provider that works this way.libsql.createrequires a non-emptyappNameon every call and always appends a random, server-generated suffix to whatever it receives, even a caller-supplied literal, so a configuration-supplied value could never match what the server actually stores. The resource seeds the create call fromnameand reads back the server’s suffixed value instead.Replica mode is modelled and its cross-field rules are enforced, but it is not functionally verified: no replica has been stood up against a real primary to confirm it actually deploys and replicates.
-
Four guides on the registry: getting started, adopting an existing Dokploy instance, deploy semantics, and secrets and sensitive values. The
-generate-config-outlimitation affecting all five database engines, and thedogfood/harness that works around it, are now documented where a provider user will find them rather than only indogfood/README.md.
Changed
- The README’s known-limitations list is redistributed. Operational warnings moved into the guide that covers them; schema and coverage gaps remain in the README under Coverage gaps. Nothing was dropped.
- The registry landing page (
templates/index.md.tmpl) still carried the old Known limitations section, in phrasings the README triage above had already superseded, and it linked to none of the four new guides. Replaced with a Guides list and the same Before you start set the README gained. The one rule that existed nowhere else in user-facing form - that a database engine owns its data mount - was carried into the adopting guide first, and now states both halves explicitly: do not import that mount, and do not declare one in fresh configuration either. - The README’s guide links are relative repository paths rather than
registry.terraform.io/.../latest/docs/guides/...URLs.latestis v0.6.0, which predates these guides, so every one of those links would have 404’d until a release ships. dokploy_project.example.environments[0].idis replaced with the order-independent[for e in ... : e.id if e.name == "production"][0]filter across the guides and all 8examples/files, and the reference docs are regenerated to match.BuildEnvironmentsappends environments in the API’s response order with no sort, so[0]is not pinned toproductionand silently misroutes once a project has a second environment; the guides now name it as an anti-pattern, so the examples had to stop demonstrating it.
[0.6.0] - 2026-07-29
Added
-
dokploy_compose: a Dokploy compose service - adocker-composeproject or a Docker Swarmstack.Exactly one of the
github,gitorrawsource blocks is required.rawcarries the compose file inline; the other two fetch it from a repository. GitLab, Bitbucket and Gitea sources are not modelled, matchingdokploy_applicationand for the same reason: no instance has been available to observe their shapes against.A
dokploy_domaincan now be attached to a compose service throughcompose_idandservice_name. That pathway has existed since v0.1.0 for a resource that did not exist yet.Three server behaviours worth knowing, all found by acceptance tests going red rather than by reading the API:
commandreplaces the deploy invocation rather than adding to it. Setting it to anything that does not itself deploy the stack makes every deploy fail.compose_pathcannot be cleared: the server rejects an empty string, so the attribute isOptional+Computedand reverts to./docker-compose.ymlrather than to null.auto_deployandtrigger_typeare genuinely nullable server-side, whileenable_submodules,randomize,isolated_deploymentandisolated_deployments_volumeare not - the latter four accept a null and silently storefalse, so they default tofalseinstead.
compose.deploy,compose.import,compose.randomizeComposeand the rest of the imperative family are deliberately not exposed, per the standing rule that imperative operations are not Terraform resources. -
dokploy_destinationdata source: resolves an existing S3-compatible backup destination bynameor byid.The use case is a shared backup target created once and referenced from several projects, so
dokploy_backup.destination_idanddokploy_volume_backup.destination_idstop being hardcoded opaque ids.access_keyandsecret_access_keyare deliberately not exposed.destination.onereturns both in cleartext, but a data source exists to be referenced, and copying a shared target’s credentials into every consumer’s state widens their blast radius for no gain. The resource still carries them.Dokploy does not enforce name uniqueness on destinations, so an ambiguous name is an error naming the match count, never
[0].
Changed
- An unexempted
types.StringPointerValueininternal/resourcesorinternal/datasourcesnow fails the build. Dokploy returns a literal""for an optional string cleared through its UI where a field never set returnsnull, andStringPointerValuepreserves the"", producing a"" -> nulldiff no apply can settle.acf76abfixed this as a manual sweep in v0.4.0; nothing enforced it until now. No user-visible behaviour change -dokploy_schedule,dokploy_backupanddokploy_volume_backupwere already correct, and now have tests saying so.
[0.5.0] - 2026-07-28
Added
-
dokploy_backup: a scheduled logical dump of a database to an S3-compatible destination.It does not accept a
redisparent, and rejects one at plan time with a message pointing atdokploy_volume_backup— Dokploy has no logical dump for Redis. Backing up the Dokploy instance itself (itsweb-serverbackup type) is also not exposed: that has no parent service and needs its own validation path.service_typederives Dokploy’sdatabaseTypeandbackupType; neither is exposed. Setting them independently is what allows a record whose type and parent disagree —backup.updateacceptsdatabaseTypewhile carrying no parent field at all, so it can flip the discriminator while leaving every id column untouched.include_encryption_keydefaults totrueand is always transmitted. Dokploy storestruefor a newly created backup butfalsewhenever an update omits the key, so a request that left it out would silently turn encryption-key inclusion off on a record created with it on. -
dokploy_volume_backup: a scheduled archive of a Docker volume to an S3-compatible destination.It accepts a redis parent, which
dokploy_backupwill not: a volume snapshot copies the volume as-is and works for any service that has one, while a logical dump needs engine support Dokploy does not have for Redis. The two routers genuinely disagree about this, and the enum sets prove it.service_idandservice_typeforce replacement —volumeBackups.updatesets the parent column it is given without clearing the others, verified live, so a retarget would leave the record owned by two services.enableddefaults totruefor the same reason asdokploy_schedule.turn_offis passed through to Dokploy’sturnOfffield and always sent concretely, because the server coerces both an absent key and an explicit null tofalse. -
dokploy_schedule: a cron job Dokploy runs against an application, a compose service, a remote server, or the Dokploy host itself.schedule_type = "dokploy-server"takes noservice_id— it runs against the Dokploy host and has no parent service. Every other type requires one. Both halves are enforced at apply time, since the rule depends on another attribute’s value and the stock config validators cannot express that.enableddefaults totrue. Dokploy leaves it null when a schedule is created through the API alone, which is neither on nor off; a schedule declared in configuration that silently never fires is the worse failure.schedule_typeandservice_idforce replacement. Dokploy’s update endpoint sets the parent column it is given without clearing the others, so a retarget would leave the record owned by two parents at once — the same defectdokploy_mountdocuments.
[0.4.0] - 2026-07-28
Added
-
dokploy_github_providerdata source, resolving a GitHub App’s name to the iddokploy_application.github.github_idexpects — so that id stops being an opaque literal pasted into configuration.Two things it does not do, both deliberate. It is not a resource: Dokploy’s API has no
github.createand installing a GitHub App is a browser flow, so a resource would lie about being able to converge. And it covers GitHub only, though Dokploy also has gitlab, bitbucket and gitea providers — none of those has been observed live (the acceptance rig has no provider of any type), and inferring three response shapes from this one is the assumption the endpoint census exists to prevent.Note
idis thegithubId, not thegitProviderId. Dokploy keeps both, an application references the former, and passing the latter is accepted by validation and then fails with an HTTP 500 because the foreign key is only enforced at the database layer. The data source exposes the generic record separately asgit_provider_id. -
dokploy_destinationresource: an S3-compatible bucket Dokploy writes backups to (Cloudflare R2, AWS, DigitalOcean Spaces, MinIO, …). The attribute isprovider_name, notprovider, becauseprovideris a reserved meta-argument in Terraform configuration.access_keyandsecret_access_keyare marked sensitive, though Dokploy stores and returns both in cleartext to anyone with API access.destination.createalso accepts aserver_id, which this resource does not expose: the read endpoints never return it, so a value written there could not be confirmed on refresh and every plan would show a diff. Exposing it needs a read path Dokploy does not currently offer. -
dokploy_port,dokploy_redirectanddokploy_securityresources, for an application’s published ports, Traefik regex redirects, and HTTP basic-auth credentials. All three share one generic implementation parameterised by aKinddescriptor: probed live, they agree on everything the resource layer touches (a singleapplicationIdparent, flat records, a dialect A update requiring the full field set, and the.deleteverb). They diverge only in their response envelopes, which stays in the client where it is visible.Two consequences worth knowing:
redirects.createandsecurity.createreturn the literaltruerather than the record, and Dokploy has no endpoint to look either up by its fields, so the provider identifies a newly created record by diffing the application’s child list around the call. Creates are serialised per application to keep that exact; if something outside the apply creates a sibling at the same moment, the provider errors rather than binding to a record it cannot prove is its own.dokploy_security.passwordis stored and returned by Dokploy in cleartext. The attribute is marked sensitive, but anyone with API access to the instance can read it.
-
dokploy_mountresource: volume, bind and file mounts attached to an application, any database engine, a compose service or a libsql instance. Two things about it are worth knowing before you use it:service_idandservice_typeforce replacement. Dokploy’smounts.updatesets the parent column you name without clearing the others, so retargeting through it leaves the record owned by two services at once (verified live, v0.29.13, 2026-07-28). The client cannot express a retarget at all.- Database services create their own data mount. A fresh
dokploy_postgresalready owns a volume mount for its data directory the moment it is created. That mount belongs to the server — do not import it or declare it here.
Per-
typefield rules (host_pathforbind,volume_nameforvolume,content+file_pathforfile) are enforced by the provider at plan time. They are provider policy, not a server contract: Dokploy accepts abindmount with no host path and stores it broken. -
dokploy_applicationgains nine operational attributes on theapplication.updatepath:auto_deploy,replicas,cpu_limit,memory_limit,cpu_reservation,memory_reservation,command,argsandregistry_id. The four resource limits are strings in Dokploy’s schema (Docker-style"0.5"/"512m"), not numbers.replicasandauto_deployare Optional+Computed with defaults of1andtrue; Dokploy’s schema has no null variant forreplicas, so it always holds a concrete value.registry_idtakes a literal id — this provider has no registry resource yet. -
dokploy_applicationgains eight attributes for fields it previously sent to the server without modelling them: top-levelwatch_paths,build_secrets(Sensitive),create_env_fileandenable_submodules;trigger_type(pushortag) inside thegithubblock; andis_static_spa,heroku_versionandrailpack_versioninsidebuild.
Fixed
-
dokploy_applicationno longer overwrites four settings on every apply. Dokploy’sapplication.save*endpoints transmit every key on every call (dialect A ininternal/client/doc.go), so any field the resource did not model was written blind. Verified live against v0.29.13 on 2026-07-28, the damage was:watch_paths— sent as an explicit JSON null on every apply, clearing whatever was configured in the Dokploy UI.build_secrets— hardcodednil, same effect.create_env_file— hardcodedtrue, so a value offalseset in the UI was silently flipped back on the next apply.trigger_type— never sent, but Dokploy applies and writes its schema default, so omitting the key overwrote the stored value rather than preserving it.
Two further fields,
enable_submodulesandis_static_spa, were never wiped — Dokploy leaves them out of the endpoint’s SQLSETlist when the request omits them — but were unmanageable. Both are now attributes.The
heroku_buildpacks/railpackbuilder version was likewise always reset to the server default;heroku_versionandrailpack_versionnow control it. -
Internal, no user-visible effect:
application.saveEnvironmentwas built as an inlinemap[string]any, which is invisible to reflection and so hid its two hardcoded literals from every guard in the client package. It is now a struct, and three tests keep this class of bug from recurring:TestEndpointFieldCensusdiffs each write endpoint’s request struct against the server’s own OpenAPI field list (distilled intointernal/client/testdata/endpoint-fields.json),TestDialectARequestsCarryNoBlindFieldsrequires every dialect A request to be a fully-tagged struct registered in both guard tables, andTestSaveRequestsReadEveryFieldFromTheModelbuilds each request from a fully-populated model and fails if any field comes out unset.
Fixed
-
Optional strings that Dokploy stores as
""no longer diff forever. Dokploy represents an unset optional string two ways in the same record: a field never set reads back as JSON null, a field set and then cleared through the UI reads back as a literal"". The provider preserved the"", while Terraform configuration that omits the attribute holds null — sodescription,env,build_args,build.build_stage,server_id, and thedokploy_domaincustom-resolver fields could each produce a"" -> nulldiff that no apply could settle.internal/resources/environmenthad the rule right forenvsince wave 2; the siblings did not. It is nowtfutil.StringOrNull, used by every resource and data source on every optional-string read path.Invisible on the acceptance rig, which creates every record through the API and therefore only ever sees null. It surfaced the first time wave 3 ran the round-trip against a production instance whose project and applications had been created through the Dokploy UI: a four-resource diff that could not be applied away.
-
dogfood/dry-run.shno longer patches an EMPTY live value into generated configuration.--patch-sensitivebackfills sensitive attributes that Terraform’s config generation leaves asnull # sensitive; writing""for a live value that is empty produced a permanentnull -> ""diff, because the provider maps""to null on read. Only Optional sensitive attributes can be legitimately empty, and for those null is already the correct encoding. Found by the same production round-trip:build_secretsis empty on both live applications.
Changed
port.onereports a missing record as HTTP 400, not 404 — the only read endpoint of six probed that does. It is now mapped to the provider’s not-found error, so a port deleted outside Terraform is reconciled as drift instead of failing the next apply. Non-not-found 400s from that endpoint stay errors.internal/client/doc.gocorrected: Dokploy does serve an OpenAPI document, atGET /api/trpc/settings.getOpenApiDocument. The old claim that it “does not serve that document at all” generalised from the/api/openapi.jsonroutes, which do 404. Its response schemas remain empty objects — hence the hand-written client — but its request field lists are complete, and the census above consumes them.
[0.3.0] - 2026-07-27
Added
dokploy_mysqlresource and data source, the first of wave 2’s additional database engines. Diverges fromdokploy_postgresin one field:database_root_passwordis server-generated when left unset, settable, and clearable, but changing it (likedatabase_password) only takes effect on the next deploy.dokploy_redisresource and data source. Redis has nodatabase_name,database_userordatabase_root_passwordfield at all — it exposes no credential attributes beyond the shareddatabase_password; its schema is the uniform database attribute set with zero engine-specific additions.dokploy_mariadbresource and data source. Field-for-field identical todokploy_mysql:database_root_passwordis server-generated when left unset, settable, and clearable, but changing it only takes effect on the next deploy. MariaDB’s server-side defaultdocker_image(mariadb:6) does not exist on Docker Hub; set an explicit tag such asmariadb:11.4.dokploy_mongoresource and data source, the last of wave 2’s database engines. Diverges from every other engine: nodatabase_nameand nodatabase_root_passwordfield at all — its only credential attribute isdatabase_user. Dokploy’s MongoDBreplicaSetsoption is not exposed as a Terraform attribute (every instance is created in standalone mode). MongoDB’s server-side defaultdocker_image(mongo:15) does not exist on Docker Hub; set an explicit tag such asmongo:7.
Changed
- Internal:
dokploy_postgresand the four new engines above now share one generic,Kind-parameterized resource and data-source implementation instead of five near-duplicates. No behavior or schema-shape change fordokploy_postgres; the only doc changes are theenvattribute’s clearing caveat (below) and its data source’s description noting that anySensitivecredential attribute is exposed but markedSensitive(true of every engine’s data source; postgres itself has none). - Names are not unique in Dokploy. Every data source that looks up by name (project, environment, application, and — as of this wave — all five database engines) errors when more than one record matches, rather than silently picking one.
domain_typenow usesUseStateForUnknown. Both of its inputs (application_id,compose_id) require replace, so it is provably immutable, and it no longer surfaces a spurious diff on the plan after an apply.- The
envattribute description, ondokploy_environmentand every database engine, now documents that omitting it and setting it to""are indistinguishable on read — both come back null; use omission, not"", to clear it. - Internal tooling, no user-visible effect:
make hooksnow works from a git worktree (it previously checked for a.gitdirectory, which a worktree does not have); the pre-commit secret scan detects a pre-8.19gitleaksbinary (missing thegitsubcommand the hook uses) and reports the version mismatch plainly instead of misreporting it as a found secret; and theapplication/project/environment/domain/postgres/deploymentclient tests now assert HTTP method and path, matching the bar every database-engine client test already held itself to.
Fixed
dogfood/dry-run.sh(this repo’s live-server round-trip harness; not part of CI) previously could not complete for any stack containing a database engine resource:terraform plan -generate-config-outrefuses to write a value for aSensitiveschema attribute, anddatabase_passwordis bothRequiredandSensitiveon every engine, so Terraform Core rejected the generated config before the provider ever ran. The harness now patches those attributes back in from the same live, read-only API it already calls, then continues the round-trip — this strengthens the assertion rather than weakening it (seedogfood/README.mdfor the full analysis). Not a provider bug:dokploy_postgreshas shipped with this exact schema shape since wave 1; the gap was simply never exercised end-to-end against a live server until this wave ran the harness against a stack containing a database engine for the first time.
[0.2.0] - 2026-07-27
Added
dokploy_environmentresource and data source. Environments sit between a project and its services; wave 0 exposed them only as a read-only list ondokploy_project.dokploy_domainresource, for attaching hostnames to applications and compose services.- Name-based lookup on the
dokploy_applicationanddokploy_postgresdata sources, viaenvironment_id+name. dogfood/, a read-only harness that checks the provider can round-trip a live stack with an empty plan.
Notes
- Dokploy refuses to delete a project’s default
productionenvironment. Destroying adokploy_environmentwithis_default = truefails with an explanatory error; useterraform state rm, or destroy the whole project. dokploy_environmenthas nocreated_at. Dokploy’s read endpoint does not return one, though its create and update endpoints do.dokploy_domain.middlewaresis read-only until the provider gains middleware resources.- Environment, application and postgres names are not unique in Dokploy. The
data sources that look them up by name error on multiple matches rather
than picking one. Domain hosts are not unique either (the same host may be
attached to more than one domain), but there is no
dokploy_domaindata source, so nothing looks a domain up by host.
[0.1.0] - 2026-07-25
Added
- Provider scaffold with
endpoint/api_key/insecureconfiguration andDOKPLOY_ENDPOINT/DOKPLOY_API_KEYfallbacks. - Resources:
dokploy_project,dokploy_application(github/git/docker sources),dokploy_postgres— all withterraform importsupport. - Data sources:
dokploy_project(by id or name),dokploy_application,dokploy_postgres. - Deploy-on-change engine:
deploy_on_change(defaulttrue) anddeployment_timeout(default15m) on service resources.
Known limitations
dokploy_applicationmanages an application’s source, build and environment configuration wholesale. Two Dokploy settings it does not expose — watch paths and build secrets — are sent as empty on every apply, so any value configured for them in the Dokploy UI is overwritten. The builder version for theheroku_buildpacksandrailpackbuild types is likewise always reset to the server default. Manage an application either in Terraform or in the Dokploy UI, not both.- Dokploy rate-limits API keys server-side and reports the limit as
401 Unauthorizedrather than429. Large configurations may need an API key with rate limiting disabled. terraform importseeds the provider-onlydeploy_on_changeanddeployment_timeoutwith their schema defaults, since nothing server-side records them.
Compatibility
- Developed and tested against Dokploy v0.29.13.