"He who would learn to fly one day must first learn to stand and walk and run and climb and dance; one cannot fly into flying."
Version: 0.6.0 · License: MIT · Python: 3.10+
Platform-agnostic deployment tooling with a provider abstraction layer. Ships with Azure, Docker/SSH, and a Custom provider that lets you define shell commands in dds.yaml — deploy to Kubernetes, Fly.io, Railway, Dokku, bare metal, or anything else without writing Python. One config file, one CLI, every environment, any cloud.
Shell scripts aren't cross-platform, testable, or maintainable at scale. DDS replaces project-embedded bash deploy scripts with a proper standalone tool:
- One
dds.yamlper project — declares all services, environments, and secrets - Preflight checks before every deploy — catches auth, tooling, and access issues early
- Health verification after every deploy — auto-checks endpoints, suggests rollback on failure
- Rollback in one command — revert to any previous Container App revision instantly
- Secrets from anywhere — Azure Key Vault,
.envfiles, environment variables, inline config - No CI required — designed for direct deploys with immediate feedback (ACR remote builds, no local Docker needed)
# From source (recommended during alpha)
pip install -e ".[dev]"# Future: PyPI
pip install dds-deploy
pipx install dds-deploy| Tool | Required | Notes |
|---|---|---|
| Python 3.10+ | ✅ | Runtime |
| Provider CLI | ✅ | az for Azure, aws for AWS, gcloud for GCP |
| Git | ✅ | Used for image tagging |
| Docker | ❌ | Only needed for build_strategy: local |
Run dds preflight to verify everything is ready.
# 1. Initialize a config file
dds init
# 2. Edit dds.yaml for your project (see Configuration below)# 3. Check prerequisites
dds preflight
# 4. Deploy everything to dev
dds deploy dev
# 5. Deploy a specific service
dds deploy dev -s api
# 6. Preview without executing
dds deploy dev --dry-runBuild, push, and deploy services. Runs preflight checks before and health verification after.
dds deploy dev # Deploy all services
dds deploy dev -s api # Deploy only 'api'
dds deploy dev -s api -s worker # Deploy multiple services
dds deploy dev --dry-run # Preview actions
dds deploy dev --skip-preflight # Skip pre-deploy checks
dds deploy dev --skip-health # Skip post-deploy verificationShow current deployment status for all services in an environment.
dds status devValidate prerequisites without deploying. Checks Azure CLI, Git, Docker, login status, and ACR access.
dds preflightRevert a Container App to a previous revision. Activates the target revision, redirects 100% traffic, and deactivates the old one.
dds rollback dev -s api # Roll back to previous revision
dds rollback dev -s api -r rev-abc # Roll back to a specific revisionShow revision history for a Container App — images, traffic weights, health state, creation times.
dds revisions dev -s apiTail or stream logs from a Container App.
dds logs dev -s api # Last 100 lines
dds logs dev -s api -f # Follow/stream in real-time
dds logs dev -s api -n 50 # Last 50 lines
dds logs dev -s api --system # Platform/system logsRun health checks on a deployed service. Verifies running state and hits the configured health_path endpoint.
dds health dev -s apiCreate a dds.yaml template in the current directory.
dds initdds --version # Show version
dds -c path/to/dds.yaml # Use a custom config path
dds -v deploy dev # Verbose outputDDS is configured via a dds.yaml file in your project root.
# DDS — Daedalus Deployment Systemproject: my-project# provider: azure # Cloud provider (azure|aws|gcp|docker) — default: azureregistry: myregistry.azurecr.io# key_vault: my-shared-keyvault # Optional: project-wide Key Vaultenvironments:
dev:
resource_group: my-dev-rgcontainer_env: my-dev-env# key_vault: my-dev-keyvault # Optional: env-level Key Vault# env_file: .env.dev # Optional: load vars from .env fileservices:
api:
type: container-appname: dev-apidockerfile: api/Dockerfilecontext: .build_strategy: acr # acr (default) or localport: 3000min_replicas: 1max_replicas: 3health_path: /healthbuild_args: # Build-time args (CACHE_BUST + GIT_HASH auto-added)NODE_ENV: productionenv: # Runtime environment variablesPUBLIC_URL: https://api.example.comsecrets: # Secrets from Key Vault or env vars
- name: DATABASE_URLvault_key: db-connection-string
- name: API_KEYenv: MY_LOCAL_API_KEYapp:
type: static-sitestorage_account: mydevstoragebuild_cmd: npm run buildbuild_dir: app/dist# install_deps: true # Auto-install node deps (default: true)env: # Build-time env (NEXT_PUBLIC_*, VITE_*, etc.)NEXT_PUBLIC_API_URL: https://api.example.comdb:
type: databaseserver: my-postgres-serverdatabase: mydb# charset: UTF8 # Default# collation: en_US.utf8 # Defaultproject: my-forgeprovider: dockerhost: my-server # SSH host (from ~/.ssh/config or IP)environments:
prod:
project_dir: /opt/forge # Remote dir with docker-compose.ymlservices:
api:
type: container-appcompose_service: api # Service name in docker-compose.ymlhealth_path: /healthport: 3000web:
type: static-sitebuild_cmd: npm run buildbuild_dir: distremote_path: /var/www/mysitedb:
type: databasecontainer: postgres # Docker container namedatabase: mydbprovider: customcommands:
container-app:
build: "docker build -t {registry}/{service}:{git_hash} ."deploy: "kubectl set image deployment/{service} {service}={registry}/{service}:{git_hash}"rollback: "kubectl rollout undo deployment/{service}"logs: "kubectl logs deployment/{service} --tail={tail} {follow_flag}"health: "kubectl rollout status deployment/{service} --timeout=60s"status: "kubectl get deployment/{service} -o wide"revisions: "kubectl rollout history deployment/{service}"static-site:
build: "npm run build"deploy: "aws s3 sync {build_dir} s3://{bucket}/ --delete"preflight:
checks:
- "kubectl cluster-info"
- "docker version"environments:
prod:
registry: myregistry.iobucket: my-prod-bucketservices:
api:
type: container-appweb:
type: static-sitebuild_dir: distAny config key becomes an interpolation variable: {host}, {port}, {registry}, {compose_file}, {bucket} — whatever you put in dds.yaml, you can use in commands. Git info ({git_hash}, {git_branch}, {build_time}) is always available.
| Type | Azure | Docker/SSH | Custom |
|---|---|---|---|
container-app | Container Apps (ACR build) | docker compose build/up over SSH | Your commands |
static-site | Blob Storage $web | rsync/scp to remote web root | Your commands |
swa | Static Web Apps | ❌ Not supported | ❌ Not supported |
database | Postgres Flexible Server | docker exec into Postgres container | Your commands |
| Strategy | Docker Required | Description |
|---|---|---|
acr (default) | No | Remote build on Azure Container Registry |
local | Yes | Local Docker build, then push to ACR |
Secrets resolve in priority layers (later layers override earlier):
env_file— Load from a.envfile (environment-level)env— Inline key-value pairs (service-level)secrets— Azure Key Vault (vault_key) or local environment variables (env)
dds/
├── dds/
│ ├── __init__.py # Version (0.4.0)
│ ├── cli.py # Click CLI — 8 commands
│ ├── config.py # dds.yaml loader + template generator
│ ├── context.py # DeployContext dataclass (with provider resolution)
│ ├── preflight.py # Generic + provider-delegated pre-deploy validation
│ ├── secrets.py # Secret resolution (provider vaults, .env, env vars)
│ ├── health.py # Backward-compat wrapper → provider.health()
│ ├── rollback.py # Backward-compat wrapper → provider.rollback()
│ ├── logs.py # Backward-compat wrapper → provider.logs()
│ ├── providers/
│ │ ├── __init__.py # Provider registry + factory
│ │ ├── base.py # Abstract base classes (ContainerProvider, etc.)
│ │ ├── azure/ # Azure provider
│ │ │ ├── container.py # Container Apps (build, deploy, rollback, logs, health)
│ │ │ ├── static.py # Blob Storage static sites
│ │ │ ├── swa.py # Static Web Apps
│ │ │ ├── database.py # Postgres Flexible Server
│ │ │ ├── secrets.py # Key Vault
│ │ │ ├── preflight.py # az login, ACR access checks
│ │ │ └── utils.py # az(), az_json() wrappers
│ │ ├── docker/ # Docker/SSH provider
│ │ │ ├── container.py # docker compose over SSH (build, deploy, logs, health)
│ │ │ ├── static.py # rsync/scp to remote host
│ │ │ ├── database.py # docker exec into Postgres containers
│ │ │ ├── secrets.py # .env file reader
│ │ │ ├── preflight.py # SSH + remote Docker checks
│ │ │ └── utils.py # ssh() wrapper, host/compose resolution
│ │ └── custom/ # Config-driven command template provider
│ │ ├── template.py # SafeFormatter, interpolation, variable builder
│ │ ├── container.py # Command-template container lifecycle
│ │ ├── static.py # Command-template static site
│ │ ├── database.py # Command-template database provisioning
│ │ ├── secrets.py # Command-template or .env fallback
│ │ └── preflight.py # Run user-defined check commands
│ ├── deployers/
│ │ ├── __init__.py # Dispatch → resolves provider + routes
│ │ ├── container.py # Backward-compat shim
│ │ ├── static.py # Backward-compat shim
│ │ ├── swa.py # Backward-compat shim
│ │ └── database.py # Backward-compat shim
│ ├── builders/
│ │ ├── __init__.py
│ │ ├── docker.py # Generic Docker build/push (local)
│ │ └── frontend.py # Frontend builds (npm/pnpm/yarn/bun auto-detect)
│ └── utils/
│ ├── __init__.py
│ ├── shell.py # Generic subprocess runner (run_cmd)
│ ├── azure.py # Backward-compat re-exports
│ └── git.py # Git info (hash, branch, build time)
├── tests/ # 40 tests
│ ├── test_builders.py
│ ├── test_cli.py
│ ├── test_config.py
│ ├── test_deployers.py
│ ├── test_preflight.py
│ └── test_secrets.py
├── pyproject.toml # Hatchling build, Click + Rich + PyYAML deps
├── LICENSE # MIT
└── README.md
- Provider abstraction — cloud-specific logic lives in
providers/<name>/; adding a new cloud means implementing the base classes, not touching core. Ships withazure,docker, andcustomproviders. - No CI dependency — deploys work from any terminal with the provider CLI and
git - Remote-first builds — ACR remote builds for Azure,
docker compose buildover SSH for Docker, extensible per provider - Preflight before, health after — catch problems on both ends of a deploy
- Auto-injection —
CACHE_BUSTandGIT_HASHbuild args added automatically - Package manager detection — lockfile-based (
pnpm-lock.yaml,yarn.lock,bun.lockb, or npm default)
# Clone
git clone git@github.com:digitalforgeca/dds.git
cd dds
# Install with dev deps
pip install -e ".[dev]"# Run tests
pytest tests/ -v
# Lint
ruff check dds/ tests/DDS was born from the TL4C project's deploy/scripts/dds — a 764-line bash script that grew organically. This is a clean-room rewrite as a standalone, testable, cross-platform tool.
The TL4C embedded DDS continues to work independently. This project does not modify or replace it.
See CHANGELOG.md for version history.
MIT — Digital Forge Studios · 2026