"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 System
project: my-project
# provider: azure # Cloud provider (azure|aws|gcp|docker) — default: azure
registry: myregistry.azurecr.io
# key_vault: my-shared-keyvault # Optional: project-wide Key Vault
environments:
dev:
resource_group: my-dev-rg
container_env: my-dev-env
# key_vault: my-dev-keyvault # Optional: env-level Key Vault
# env_file: .env.dev # Optional: load vars from .env file
services:
api:
type: container-app
name: dev-api
dockerfile: api/Dockerfile
context: .
build_strategy: acr # acr (default) or local
port: 3000
min_replicas: 1
max_replicas: 3
health_path: /health
build_args: # Build-time args (CACHE_BUST + GIT_HASH auto-added)
NODE_ENV: production
env: # Runtime environment variables
PUBLIC_URL: https://api.example.com
secrets: # Secrets from Key Vault or env vars
- name: DATABASE_URL
vault_key: db-connection-string
- name: API_KEY
env: MY_LOCAL_API_KEY
app:
type: static-site
storage_account: mydevstorage
build_cmd: npm run build
build_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.com
db:
type: database
server: my-postgres-server
database: mydb
# charset: UTF8 # Default
# collation: en_US.utf8 # Defaultproject: my-forge
provider: docker
host: my-server # SSH host (from ~/.ssh/config or IP)
environments:
prod:
project_dir: /opt/forge # Remote dir with docker-compose.yml
services:
api:
type: container-app
compose_service: api # Service name in docker-compose.yml
health_path: /health
port: 3000
web:
type: static-site
build_cmd: npm run build
build_dir: dist
remote_path: /var/www/mysite
db:
type: database
container: postgres # Docker container name
database: mydbprovider: custom
commands:
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.io
bucket: my-prod-bucket
services:
api:
type: container-app
web:
type: static-site
build_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