dockyard-cli
v0.1.0
Published
Platform-agnostic infrastructure as code for PaaS. Manage Dokploy, Railway, and other platforms from a config file.
Maintainers
Readme
Dockyard
Dockyard is a CLI tool that manages PaaS infrastructure from a config file. You define your projects, applications, databases, and domains in dac.config.ts, and the tool creates, updates, or deletes resources on your provider to match. It currently supports Dokploy and Railway. The provider interface is generic, so other platforms can be added.
Your project can be written in any language. The dac.config.ts file is just a config that sits in your repo.
Install
Requires Bun.
bun add -g dockyard-cliThat's it. You now have the dac command available everywhere.
If you don't want a global install, you can use bunx to run it without installing:
bunx dockyard-cli init
bunx dockyard-cli plan
bunx dockyard-cli applyOr build from source:
git clone https://github.com/Nu11ified/Dockyard.git
cd Dockyard && bun install && bun run build
cp dist/dac ~/.local/bin/Quick start
1. Create a config file in your project:
dac initThis creates dac.config.ts in the current directory. It works in any project: Go, Python, Rust, a static site, anything.
2. Set your provider credentials:
Dokploy:
export DOKPLOY_URL="https://your-dokploy-instance.com"
export DOKPLOY_API_KEY="your-api-key"Get your API key from the Dokploy dashboard under Settings > API.
Railway:
export RAILWAY_TOKEN="your-railway-api-token"Get your token from the Railway dashboard.
3. Edit dac.config.ts:
export default {
providers: {
dokploy: {
type: "dokploy",
url: process.env.DOKPLOY_URL!,
apiKey: process.env.DOKPLOY_API_KEY!,
},
},
project: {
name: "my-app",
},
environments: {
production: {
provider: "dokploy",
applications: [{
name: "api",
source: { type: "github", repo: "myorg/api", branch: "main", owner: "myorg" },
build: { type: "dockerfile", context: "." },
domains: [{ host: "api.example.com", https: true }],
env: {
NODE_ENV: "production",
DATABASE_URL: "${{db.postgres.connectionString}}",
},
ports: [{ container: 3000 }],
}],
databases: [
{ name: "postgres", type: "postgres", version: "16" },
],
},
},
};No imports needed. The CLI validates the config at runtime.
To use Railway instead, swap the provider:
providers: {
railway: {
type: "railway",
token: process.env.RAILWAY_TOKEN!,
teamId: "optional-workspace-id", // optional
},
},The rest of the config (applications, databases, domains) stays the same. See Platform differences for what each provider supports.
4. Preview and apply:
dac plan # show what would change
dac apply # apply the changes5. Deploy:
dac deploy # deploy all applications
dac deploy api # deploy a specific appTypeScript autocomplete (optional)
If you want autocomplete and type checking in your config, add dockyard-cli as a dev dependency in a JS/TS project:
bun add -d dockyard-cliThen add a type annotation to your config:
import type { DacConfig } from "dockyard-cli";
export default {
// full autocomplete here
} satisfies DacConfig;This is optional. The config works without it.
Commands
| Command | Description |
|---|---|
| dac init | Create a dac.config.ts template |
| dac plan | Show planned changes without applying |
| dac apply | Apply changes (prompts for confirmation) |
| dac apply --auto-approve | Apply without confirmation |
| dac deploy [app] | Trigger deployment for one or all apps |
| dac destroy | Delete all managed resources |
| dac status | List managed resources and their IDs |
| dac rollback <app> | Rollback an application |
Configuration reference
Provider types
dokploy— Self-hosted Dokploy instance. RequiresurlandapiKey.railway— Railway.com. Requirestoken. OptionalteamIdfor workspace scoping.
Source types
github- repo, branch, owner, buildPath, watchPaths, triggerTypegitlab- repo, branch, owner, buildPath, projectId, pathNamespacebitbucket- repo, branch, owner, repositorySlug, buildPathgitea- repo, branch, owner, buildPathgit- url, branch, buildPath, sshKeyIddocker- image, registryUrl, username, password
Build types
dockerfile- dockerfile, context, buildStagenixpacks- no extra optionsbuildpacks- versionstatic- publishDirectory, isSparailpack- version
Database types
postgres, mysql, mariadb, redis, mongo
Each takes name, type, and an optional version. Credentials are auto-generated if not specified.
Resource references
Use ${{db.<name>.<field>}} in environment variables to reference database connection details. These are resolved after databases are created.
env: {
DATABASE_URL: "${{db.postgres.connectionString}}",
REDIS_URL: "${{db.cache.connectionString}}",
}Other resource types
Applications can also include: domains, ports, mounts, redirects, security, resources (CPU/memory), replicas.
Top-level config also supports certificates and registries arrays.
Platform differences
Not all features are available on every provider:
| Feature | Dokploy | Railway | |---|---|---| | Applications | Yes | Yes | | Databases | Yes | Yes (auto-created as services) | | Compose | Yes | No | | Domains | Yes | Yes (SSL automatic) | | Mounts/Volumes | Yes | Yes | | Redirects | Yes | No | | Basic auth | Yes | No | | Manual certificates | Yes | No (SSL automatic) |
Using an unsupported feature with a provider that doesn't support it will produce an error during dac plan.
GitHub Actions
Since dac.config.ts is just TypeScript, it can read process.env directly. Any environment variable you set in your GitHub Actions workflow is available in your config. This means you can store secrets (API keys, database passwords, tokens) in GitHub and have them flow into your deployments without hardcoding anything.
Basic setup
Create .github/workflows/deploy.yml:
name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Plan
if: github.event_name == 'pull_request'
run: bunx dockyard-cli plan
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Apply
if: github.event_name == 'push'
run: bunx dockyard-cli apply --auto-approve
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Deploy
if: github.event_name == 'push'
run: bunx dockyard-cli deploy
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}Then in your config, reference them:
applications: [{
name: "api",
// ...
env: {
STRIPE_KEY: process.env.STRIPE_KEY!,
OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
},
}],Add your secrets in your GitHub repo under Settings > Secrets and variables > Actions.
Scoped environments (staging / production)
GitHub Actions has an environments feature that lets you define separate sets of secrets per environment. Each environment gets its own secrets, so STRIPE_KEY in staging can be a test key while production uses the live key.
name: Deploy
on:
push:
branches: [main, develop]
jobs:
staging:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Apply staging
run: bunx dockyard-cli apply --auto-approve
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
APP_ENV: staging
- name: Deploy staging
run: bunx dockyard-cli deploy
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}
production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Apply production
run: bunx dockyard-cli apply --auto-approve
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
APP_ENV: production
- name: Deploy production
run: bunx dockyard-cli deploy
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}Your config can then branch on APP_ENV or use different providers per environment. For example, Dokploy for staging and Railway for production:
export default {
providers: {
dokploy: {
type: "dokploy",
url: process.env.DOKPLOY_URL!,
apiKey: process.env.DOKPLOY_API_KEY!,
},
railway: {
type: "railway",
token: process.env.RAILWAY_TOKEN!,
},
},
project: { name: "my-app" },
environments: {
staging: {
provider: "dokploy",
applications: [{
name: "api",
source: { type: "github", repo: "myorg/api", branch: "develop", owner: "myorg" },
build: { type: "dockerfile" },
env: {
NODE_ENV: "staging",
STRIPE_KEY: process.env.STRIPE_KEY!,
},
}],
databases: [
{ name: "postgres", type: "postgres", version: "16" },
],
},
production: {
provider: "railway",
applications: [{
name: "api",
source: { type: "github", repo: "myorg/api", branch: "main", owner: "myorg" },
build: { type: "dockerfile" },
env: {
NODE_ENV: "production",
STRIPE_KEY: process.env.STRIPE_KEY!,
},
}],
databases: [
{ name: "postgres", type: "postgres", version: "16" },
],
},
},
};The same database and application config works on both providers. Dockyard handles the platform-specific translation.
To set this up:
- Go to your GitHub repo > Settings > Environments
- Create
stagingandproductionenvironments - Add secrets to each (same key names, different values)
- Push to
developto deploy staging, push tomainto deploy production
The same config file handles both. The secrets are different per environment because GitHub injects the right set based on the environment: field in the workflow.
The .dac/state.json file should be committed to your repo so CI knows what resources are already managed.
How it works
Dockyard keeps a state file at .dac/state.json that maps config resource names to remote IDs. On dac plan or dac apply, it diffs your config against this state and produces creates, updates, and deletes. Resources are applied in dependency order: projects, then environments, then databases, then applications.
Commit the state file to your repo. This is how Dockyard tracks what it manages.
Development
git clone https://github.com/Nu11ified/Dockyard.git
cd Dockyard
bun install
bun test # run tests
bun run dev # run CLI without compiling
bun run build # compile to ./dist/dacLicense
MIT
