Skip to content

feat: add declarative apply & export commands#30

Draft
nurikk wants to merge 20 commits intoDokploy:mainfrom
nurikk:feat/declarative-apply
Draft

feat: add declarative apply & export commands#30
nurikk wants to merge 20 commits intoDokploy:mainfrom
nurikk:feat/declarative-apply

Conversation

@nurikk
Copy link

@nurikk nurikk commented Mar 16, 2026

Summary

Adds two new commands to the Dokploy CLI for managing resources as code (ref: Dokploy/dokploy#3872, Dokploy/dokploy#4008):

  • dokploy apply -f dokploy.yaml — Apply a declarative YAML config to create or update resources on the server
  • dokploy export -p <project> -f out.yaml — Export an existing project as a dokploy.yaml config file

These commands enable round-tripping: export a project, check the YAML into git, modify it, and apply changes back.

apply command

dokploy apply -f dokploy.yaml              # Apply changes
dokploy apply -f dokploy.yaml --dry-run    # Preview changes without applying
dokploy apply -f dokploy.yaml --verbose    # Show field-level diffs
  • Parses and validates YAML against a Zod schema
  • Fetches current server state and diffs against the config
  • Creates or updates resources (never deletes — safe by default)
  • Name-based matching (project + environment + resource name)
  • Two-step creation: create with minimal fields, then update with full config
  • Supports all resource types: applications, compose, postgres, mysql, mariadb, mongo, redis, domains, ports, mounts, redirects, security rules, schedules
  • Optional serverName field for multi-server deployments
  • Env vars managed separately via existing dokploy env push

export command

dokploy export                              # Interactive project selection, stdout
dokploy export -p my-project                # By name, stdout
dokploy export -p my-project -f out.yaml    # Write to file
  • Fetches all project resources including child resources (domains, ports, mounts, etc.)
  • Strips server-generated fields (IDs, timestamps, status)
  • Omits default values for cleaner output
  • Resolves serverIds back to serverNames
  • Output matches the apply schema exactly (round-trip safe)

Architecture

src/lib/apply/
├── types.ts          # TypeScript interfaces
├── schema.ts         # Zod validation schema
├── parser.ts         # YAML parse + validate
├── api-client.ts     # Typed tRPC endpoint wrappers
├── differ.ts         # Config vs remote state diffing
├── reporter.ts       # Terminal output formatting
├── executor.ts       # Create/update API execution
├── reconciler.ts     # Apply pipeline orchestration
└── exporter.ts       # Export transformation
src/commands/
├── apply.ts          # Apply command
└── export.ts         # Export command

New dependencies

  • yaml — YAML parsing and serialization
  • zod — Schema validation

Test plan

  • 32 unit tests passing (schema validation, YAML parsing, diffing, reporting, export transformation)
  • Full TypeScript build succeeds
  • Both --help commands render correctly
  • Manual test: export an existing project, then apply the exported YAML
  • Manual test: dry-run against a live server
  • Manual test: apply with changes and verify update detection

@nurikk nurikk marked this pull request as draft March 16, 2026 09:08
nurikk added 5 commits March 16, 2026 10:24
- Use .one endpoints (application.one, postgres.one, etc.) to fetch full
  resource details with child resources instead of non-existent list
  endpoints (port.byApplicationId, redirect.byApplicationId, etc.)
- Fix mount router name from 'mount' to 'mounts' (matching server registration)
- Fix project ID extraction from createProject response (nested under .project)
- Handle pre-existing default environment when creating projects
- Fix port publishMode default from 'host' to 'ingress' (matching server behavior)
- Clean up exporter to strip git-provider fields, runtime fields, and
  auto-created database mounts for cleaner YAML output
resolveServer() adds _resolvedServerId to config objects, but it was
not in IGNORED_FIELDS. This caused spurious "update" actions for every
resource with a serverName, since remote objects never contain this
internal field.
- Add environment update support: executeEnvironmentAction now handles
  "update" actions instead of silently dropping them
- Preserve schedule enabled field: stop stripping `enabled` from exports
  so disabled schedules survive export/apply roundtrip (use DEFAULTS
  to omit only when true)
- Narrow mount duplicate detection: only swallow 409 or 400 with
  "already" message, not all 400 errors
- Guard createProject response: throw immediately if projectId is
  missing instead of propagating undefined
Move the API client from src/lib/apply/api-client.ts to src/utils/api.ts
so it can serve as the canonical HTTP layer for the entire CLI, not just
the apply/export commands.

- src/utils/api.ts: canonical location with trpcQuery/trpcMutation helpers
- src/lib/apply/api-client.ts: re-exports from utils/api for compatibility
- src/utils/shared.ts: rewritten to use api client instead of raw axios
- src/lib/apply/exporter.ts: removed duplicate fetchProject function
All 30 command files now use src/utils/api.ts instead of direct axios
calls. axios is imported in exactly one place.

- Migrated: authenticate, verify, project/create, environment/create,
  environment/delete, env/push, app/* (4 files), database/*/* (20 files)
- Added lifecycle endpoints to api.ts: deploy, stop, remove for all
  service types; getUser for auth validation
- Removed unused src/utils/http.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant