Skip to main content

⌨️ CLI reference

npx @django-stack/cli <command>
dstack <command> # once it is installed

dstack help <command> or <command> --help lists the options of a command. Every prompt has a flag, so each command also runs in scripts and CI.

Install dstack​

The first time you run the CLI with npx, pnpm dlx or bunx, it asks when the command is done (init: before its next steps) whether to install dstack globally, with the package manager you ran it with. After that, dstack init does the same as npx @django-stack/cli init. You can also install it yourself:

npm i -g @django-stack/cli

The CLI asks only once and remembers the answer in ~/.config/django-stack/config.json (or under $XDG_CONFIG_HOME). It does not ask when dstack is already installed, in CI, when the input or output is not a terminal, or with --yes. Turn it off with --no-install-prompt or DJANGO_STACK_NO_INSTALL_PROMPT=1.

When the install fails, the command still succeeds and the CLI prints the reason and the command to run yourself. A permission error usually means npm's global directory belongs to root: give npm a directory in your home instead of using sudo. With pnpm, run pnpm setup first.

self-update​

Updates dstack to the latest version on npm, with the package manager that installed it: npm, pnpm, Bun or Yarn.

dstack self-update

It runs npm i -g @django-stack/cli@latest, pnpm add --global, bun add --global or yarn global add, depending on where dstack is installed. For npm, it adds the --prefix of the current install, so it updates that copy also when another Node.js version comes first on your PATH. When you have the latest version already, it changes nothing. When the update fails, it exits with status 1 and prints the reason and the command to run yourself. When the dstack on your PATH is still an older version afterwards, another install comes first, and it warns you.

With npx, pnpm dlx or bunx there is nothing to update: run npx @django-stack/cli@latest instead.

init​

Creates a project in a new directory.

npx @django-stack/cli init [directory] [options]
OptionDescription
--name <name>The project name: lowercase letters, digits and dashes. The default comes from the directory name.
--preset <preset>Set the stack options below from a preset code or name. See Presets.
--database-url <url>Your own Postgres for production, saved as PRODUCTION_DATABASE_URL: production uses it instead of the bundled one. Development, and dstack run, keep the bundled Postgres. URLs to other hosts get sslmode=require unless they set an sslmode.
--time-zone <zone>The time zone of the backend, an IANA name such as Europe/Amsterdam. The prompt suggests this computer's zone and filters as you type. The default with --yes is UTC.
--no-demo-contentStart with an empty site.
--no-gitDon't create a git repository with a first commit.
--startRun docker compose up when the project is created.
-y, --yesDon't ask; use the defaults for every option that is not given.

init starts by checking this computer:

  • Docker is installed and running, with Docker Compose v2;
  • Node.js 22 or later;
  • Python 3.12 or later.

A failed check is a warning with the install command for your operating system: Homebrew on macOS, winget on Windows, and the install scripts on Linux. Node.js is installed with fnm and Python with uv. The project is created anyway, because the project runs in Docker and Node.js and Python are only for development outside the containers. With --start, init does not start the project when Docker is not ready.

Stack options​

Each stack option is asked for, unless a flag or --preset sets it. The first value is the default. Values marked later are in the list already, but this release does not generate them yet: init refuses them, and lists the defaults it does not generate yet when it is done.

OptionValues
--frontendastro, next, headless (just the API and the admin, see Headless), templates (Django renders the site, see Django templates), nuxt, sveltekit
--postgresstable (Postgres 17), latest (Postgres 18). It also sets the version of the Postgres client in the backend image, for the backups. With --database-url, pick your server's version.
--analyticsumami (Umami, see Page analytics), off
--taskshuey (a worker, with its queue where the cache is: Postgres, or Valkey with --cache valkey), off (tasks run in the request, no worker and no daily backups), huey-valkey (the queue in Valkey with any cache)
--cachedatabase (a table in Postgres, nothing extra to run), valkey (the Valkey service), off (a cache per process)
--notificationson (in the admin and by email, see Notifications), off
--captchaon (a proof of work on the contact form, see Captcha), off
--storagelocal, s3 (a local S3 service in development, the same code path as S3 in production; see Media and database)
--package-managerpnpm (resolves only versions published a day ago), npm, bun (installs with Bun; the frontend runs on Node.js). Only for a Node frontend. See Package managers.
--mediaserver (uploads in a volume on the server), s3 (uploads in S3 in production; settings asks for the credentials). Asked before --storage, which then suggests s3. See Media in S3.
--errorsemail (each new error by email to the superusers and in the admin, see Error reports), sentry (to sentry.io with SENTRY_DSN, see Sentry), off (the logs only)

Package managers​

--package-manager picks what installs the frontend's packages. init writes its lockfile through npx at the version the image uses, so you don't need the package manager on your computer, and the frontend's Dockerfile installs from that lockfile. The frontend runs on Node.js either way, and npm, which comes with Node.js, runs the package.json scripts in the image.

ValueWhat init writes
pnpmpnpm-lock.yaml, packageManager in frontend/package.json, and frontend/pnpm-workspace.yaml, which resolves only versions published at least a day ago (minimumReleaseAge, except the @django-stack packages) and lists the packages that may run install scripts (allowBuilds). The image installs pnpm with npm.
npmpackage-lock.json.
bunbun.lock and packageManager in frontend/package.json. The image copies the bun binary from Bun's image for the install only.

config can't switch the package manager. To switch by hand, delete the lockfile (and pnpm-workspace.yaml when you leave pnpm), install with the other package manager in frontend/, and change the install lines of the deps stage in frontend/Dockerfile.

Presets​

A preset sets every stack option at once, so you can share a stack or create the same one again. When init is done, it prints the preset of the project, a code of six letters and digits. This one is Next.js without analytics:

npx @django-stack/cli init my-site --preset b7mWME

Flags win over the preset, so --preset b7mWME --storage s3 changes only the storage. Shorter codes that start with a still work. There are named presets too:

NameStack
blogAstro with everything on: the defaults.
apiHeadless, without analytics.
minimalAstro with analytics, tasks, cache, notifications and captcha off.

dstack preset asks for the stack options and prints the code, and dstack preset <code> shows the stack of a code. A mistyped code, or one from a newer CLI, fails with a request to check it or to update.

The command refuses a directory that is not empty. When uv is installed, it also locks the backend's dependencies in backend/uv.lock.

Project commands​

These commands run in a project created by init, from its directory or any directory below it. -C <directory> (--dir) points them at another project. They need Docker Compose v2. When Docker is not running, they stop with Docker not running. and how to start it.

CommandWhat it does
dstack devStarts the development servers in the foreground, with hot reload. Ctrl+C stops them. A port in use moves to the next free one, saved in .env.
dstack runStarts the production setup in the background and waits until it is healthy.
dstack stopStops the containers. The database, uploads and backups stay in their volumes.
dstack logs [service...]Follows the logs. --no-follow prints them and exits, -n <lines> shows only the last lines.
dstack statusShows the containers, then whether the database, the cache, the worker and the daily backup are healthy. Exits 1 when one is not. See Health checks.
dstack checkChecks the project. See check.
dstack updateUpdates the images and the DjangoStack packages. See update.
dstack configChanges the stack options. See config.
dstack manage <command>Runs a Django management command in the backend, such as dstack manage createsuperuser.
dstack shellOpens Django's shell in the backend.
dstack backupBacks up the database and, when uploads are on disk, the media now. --list lists the backups, --no-media skips the media.
dstack restore <backup>Restores a backup from dstack backup --list over the database. --media also restores its media; a .media.tar.gz name restores only the media. It asks first; --yes doesn't. See Backups.
dstack typesGenerates the frontend's API types in frontend/src/lib/headless.ts from the backend's schema; in a headless or Django templates project it writes the schema to backend/openapi.json.
dstack deployDeploys to a hosting platform and shows the status. See deploy.

manage, shell, logs and check use the running containers, and start a one-off container when the backend is not running.

dev and run​

dstack dev runs the dev profile: Django's development server and the frontend's, with the source mounted from backend/ and frontend/. It is docker compose up --build. --no-build skips the build.

dstack run runs the prod profile: the production images behind Caddy, as on a server. On a server, where .env has the prod profile, it is docker compose up -d --build --wait.

On your computer, dstack run tries the production setup without touching your development data. The first time, it writes .env.run from .env with the prod profile, SITE_URL=https://site.localhost, mail to the console and new secrets, and prints the admin password. It runs as its own Compose project (<name>-run), so it has its own database and uploads. Open https://site.localhost. Caddy signs it with a local certificate authority of its own, so the browser warns once. Ports 80 and 443 must be free.

While the local run is up, logs, manage, shell, check, backup and restore act on it. dstack stop stops both. Delete .env.run to start over with new secrets; docker compose --env-file .env.run -p <name>-run down --volumes deletes its data too.

check​

Runs the doctor's checks (see doctor), then in the containers:

  • Django's system checks, or the deploy checks with --fail-level WARNING in the prod profile.
  • That the migrations are up to date (makemigrations --check).
  • Ruff on the backend and the frontend's type checks (npm run check), in development.
  • With --tests, the backend's and the frontend's tests, in development.

It prints the output of each check that fails and exits with status 1.

update​

  1. With uv installed, upgrades django-stack in backend/uv.lock. --all upgrades every backend package.
  2. Upgrades the @django-stack packages in the frontend's lockfile (pnpm-lock.yaml, package-lock.json or bun.lock) with its package manager, within the ranges in frontend/package.json. --all upgrades every frontend package.
  3. Pulls the newer images of the services (Postgres, Valkey, Caddy and the others) and rebuilds the backend and frontend images on fresh base images. --no-cache rebuilds them from scratch.
  4. In development, empties the frontend's node_modules volume, so the next start uses the packages of the new image.
  5. Restarts what was running, unless you pass --no-restart.

Commit the changed lockfiles. On a server, git pull first, then dstack update.

config​

Changes the stack options of a project: it asks for each option it can change, or takes the same flags as init, such as --storage s3. It updates .env and the preset in package.json, then asks you to restart.

This release can switch --storage between local and s3 in development, --analytics between umami and off (see Page analytics), --media between server and s3, which changes only the preset, --notifications between on and off, which adds or removes the app in backend/config/settings.py, --captcha between on and off, which sets CAPTCHA in .env, and --cache and --tasks, which also turn the valkey profile on or off. Queued tasks and cached values stay behind in the old place, so switch while the queue is empty. The other options can only be set by init; config refuses them with the reason. For another Postgres version, see Upgrade Postgres.

production​

Writes .env.production, the settings for a server, from the project's .env.

npx @django-stack/cli production [directory] --domain example.com [options]
OptionDescription
--domain <domain>The domain of the site. Asked for when it is not given.
--email-url <url>The SMTP server, such as smtp+tls://user:password@smtp.example.com:587. Without it, EMAIL_URL gets a placeholder to fill in.
--database-url <url>Your own Postgres for the server: .env.production gets it as DATABASE_URL, without db in COMPOSE_PROFILES. URLs to other hosts get sslmode=require unless they set an sslmode. Without it, and without PRODUCTION_DATABASE_URL in .env, it asks.
--forceReplace an existing .env.production.
-y, --yesDon't ask; fail when --domain is missing.

It switches COMPOSE_PROFILES to the prod profile, sets SITE_URL and BACKEND_URL to https://<domain>, turns debug off and generates new secrets for everything, including the admin password and the bundled database's password. The local S3 service's values are cleared. It then prints what is left to fill in and the admin password. The file is readable only by you. See Deploy to a server.

In a terminal, it asks whether the server uses the bundled Postgres or your own. For your own, you enter either a postgres:// URL or the host, port, database name, user and password separately. Separate values are URL-encoded for you, so use them when the password has characters such as @, :, / or #. See An external database.

In a terminal, it then offers to ask for the missing settings, as settings does.

settings​

Asks for the production settings and credentials that are missing in .env.production, one at a time, and saves them there.

dstack settings [options]
OptionDescription
-C, --dir <directory>The project directory (default: this one or a parent).
--file <file>The file to change. The default is .env.production, or .env when it runs the prod profile, as on a server.
--set <NAME=value>Set a value without asking. Repeat it for more values.
--allAsk for every setting, also the ones that are set. Each answer starts with the current value; an empty secret keeps the current one.
-y, --yesDon't ask. With settings still missing, it lists them and exits 1.

It asks for:

SettingWhenSaved as
Your email addressADMIN_EMAIL is empty or at example.comADMIN_EMAIL
The SMTP server, port, user name and passwordEMAIL_URL is empty, Mailpit or the placeholderEMAIL_URL, such as smtp+tls://user:password@smtp.example.com:587: smtps:// on port 465, smtp:// on port 25
The S3 bucket, endpoint, region, access key and public URLuploads go to S3 (init --media s3, or S3_BUCKET is set) and one of them is emptyS3_BUCKET, S3_ENDPOINT_URL, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_PUBLIC_URL
dstack settings --yes \
--set ADMIN_EMAIL=you@example.com \
--set EMAIL_URL=smtp+tls://user:password@smtp.example.com:587

Passwords are hidden as you type. A value with $, # or quotes is saved in single quotes, so Docker Compose reads it as it is. When the project is linked to a platform, push the new values with dstack deploy env push.

deploy​

Puts the project on a hosting platform or a server, deploys it and shows its status. There are four kinds of providers:

  • Coolify (--provider coolify, the default) and Dokploy (--provider dokploy) build the project on their server from the git repository, so push your commits before you deploy. See Coolify and Dokploy.
  • A server over SSH (--provider ssh) gets your last commit uploaded and runs docker compose up there, so commit before you deploy. See Deploy to a server.
  • Bunny Magic Containers (--provider bunny) gets the images of your last commit built on your machine and pushed to Bunny Container Registry, so commit before you deploy. See Bunny Magic Containers.
  • Railway (--provider railway) builds the backend and the frontend from the GitHub repository and deploys again on every push, so push your commits before you deploy. See Railway.
dstack production --domain example.com
dstack deploy setup --url https://coolify.example.com
dstack deploy
CommandWhat it does
dstack deploy setupCreates the project and the application on the platform, sets the domain and uploads the variables. See deploy setup.
dstack deployStarts a deploy and follows it until it ends; on a failure it prints the end of the build log. --force rebuilds without the cache, --no-wait returns once the deploy started. dstack deploy start is the same command.
dstack deploy statusShows whether the site runs, its domain, and the last deploys. --watch follows a running deploy, -n <count> lists more deploys.
dstack deploy logs [deployment]Prints the build log of a deploy, the latest by default.
dstack deploy rollback [deployment]Starts an earlier release again: the one before the running one, or the deploy you name. Servers over SSH, Bunny and Railway only. --no-wait returns once it started.
dstack deploy env pushReplaces the platform's variables with .env.production.
dstack deploy loginChecks an API key and saves it. setup asks for one when there is none. Not used for servers over SSH.
dstack deploy logoutRemoves a saved API key.

dstack deploy refuses when the deploy would not include your work: for Coolify, Dokploy and Railway, when your last commit is not on the branch it deploys (push it); for a server over SSH and Bunny, when you have uncommitted changes (commit them). --no-push-check deploys anyway.

deploy setup​

OptionDescription
--provider <provider>coolify, dokploy, ssh, bunny or railway. Asked for when it is not given; with --yes, coolify.
--url <url>The platform's address, such as https://coolify.example.com, or the server, such as deploy@example.com or deploy@example.com:2222.
--repository <url>The git repository the platform builds. Default: the origin remote, as an https URL.
--branch <branch>The branch to deploy. Default: the current branch.
--server <server>The Coolify or Dokploy server, by name or id. Asked for when there are several. For Dokploy, dokploy is its own server, the default.
--project <project>The Coolify, Dokploy or Railway project, by name or id. Default: a new project named after the project.
--environment <name>The Coolify, Dokploy or Railway environment. Default: production, created when it does not exist.
--workspace <workspace>The Railway workspace, by name or id. Asked for when there are several.
--github-app <app>The Coolify GitHub App that reads a private repository, or public. Asked for when Coolify has GitHub Apps.
--path <path>The project directory on a server over SSH. Default: /srv/<project>.
--region <region>The Bunny region, such as DE. Default: the closest.
--volume-size <GB>The size of Bunny's database volume. Default: 10.
--account <id>The Bunny account id, the namespace in Bunny Container Registry. Asked for when Bunny does not list it.
--deploy, --no-deployDeploy when done, or don't ask.
--forceSet up again, replacing .dstack/deploy.json.
-y, --yesDon't ask; use the flags and the defaults.

It reads the variables and the domain from .env.production, so run production first. For Coolify and Dokploy, it adds the variables that put Caddy behind the platform's proxy (see Coolify and Dokploy), creates a Docker Compose application from the repository with compose.yaml, and gives the proxy service the domain. For a server over SSH, it checks Docker and the Compose plugin, creates the project directory and uploads the variables as shared/.env. For Bunny, it builds and pushes the images, creates one Magic Containers app with a container per service and adds the domain to its CDN; Bunny has a fixed API, so there is no --url. For Railway, it creates a project with one service per production service and gives the proxy service the domain; Railway also has a fixed API.

It then writes .dstack/deploy.json: the platform's address and the ids of the project, environment and application, or the server, its directory and the Compose project name. The file holds no secrets; commit it, so everyone who works on the project deploys to the same place. After setup, the platform keeps the variables: change them there, or change .env.production and run dstack deploy env push.

API keys​

The CLI saves an API key in the system keychain (Keychain on macOS, Credential Manager on Windows, the Secret Service on Linux), per platform address, and never in the project. Where there is no keychain, such as on a server without a desktop, it saves the key in ~/.config/dstack/credentials.json, readable only by you, and says so.

In CI, set DSTACK_COOLIFY_TOKEN, DSTACK_DOKPLOY_TOKEN, DSTACK_BUNNY_TOKEN or DSTACK_RAILWAY_TOKEN: it overrides a saved key and nothing is written. Create a Coolify key under Keys & Tokens with the read, write and deploy permissions; the API must be on under Settings › Advanced. Create a Dokploy key under Settings › Profile › API/CLI. Bunny uses the account API key from Account › API key. Railway uses an account or workspace token from Account Settings › Tokens. Servers over SSH use your SSH keys and need no API key.

doctor​

Checks a project and its running services.

npx @django-stack/cli doctor [directory]

It checks that Docker and Docker Compose v2 are available, that Node.js and Python are recent enough (a warning only, as in init), that .env has every required value and two different API keys, and, when the project is running, that the backend is healthy, the site at SITE_URL answers, the read-only key reads the API and the API is closed without a key. With the prod profile it also checks that SITE_URL is https and that mail goes to an SMTP server. It exits with status 1 when a check fails.

secret​

Prints a random URL-safe secret, for example to rotate a value in .env.

npx @django-stack/cli secret [bytes]

The default is 32 bytes.

completion​

Prints a script that completes commands, options and their values when you press Tab. It needs dstack on your PATH; see Install dstack.

dstack completion zsh|bash|fish|powershell

Add it to your shell's startup file:

ShellLine
zsh, in ~/.zshrcsource <(dstack completion zsh)
bash, in ~/.bashrcsource <(dstack completion bash)
fish, in ~/.config/fish/config.fishdstack completion fish | source
PowerShell, in $PROFILEdstack completion powershell | Out-String | Invoke-Expression

Update check​

Each command checks npm, at most once a day, for a newer version of the CLI and shows a notice with the command to update when there is one: dstack self-update, or npx @django-stack/cli@latest with npx. The check runs while the command runs and never makes it fail. It is cached in ~/.cache/django-stack/update-check.json (or under $XDG_CACHE_HOME).

It is skipped in CI, when the output is not a terminal, and for secret, completion and self-update. Turn it off with --no-update-check or by setting DJANGO_STACK_NO_UPDATE_CHECK=1 (NO_UPDATE_NOTIFIER=1 works too).

With npx, ask for the latest version to skip a cached older copy:

npx @django-stack/cli@latest init