Deploying with Git Push
Every Web Application or Background Worker gets a source repository hosted on the platform. You push your code to it, the platform builds an image from your source with buildpacks, and the new version is deployed to the app’s Environment. This works the same whether your code is public or private. Pushing to this repository is one way in; you can also link a GitHub or GitLab branch so the platform deploys from it (see Deploy from GitHub or GitLab).
This guide assumes you already have an app. If you don’t, see Creating a Web Application first. Pushing requires deploy access to the app’s Project.
The examples use the paas CLI with org acme, project shop, environment production, and an app named web. Add -e production to the commands (the environment has no configurable default; it is only inferred from an existing paas or d2o remote), plus -o acme -p shop if you haven’t set defaults (paas orgs set-default, paas projects set-default). See Installing the CLI.
Upload an SSH key
Pushing over SSH authenticates with an SSH public key attached to your account. A key lets you push to any app repository you have deploy access to.
In the dashboard: open Account Settings, then under SSH Keys click Add Key. Give the key a name (for example laptop), paste your public key (the contents of a .pub file, e.g. ssh-ed25519 AAAA... user@host), and save. The list shows each key’s fingerprint; deleting a key immediately stops it from pushing.
With the CLI:
paas keys add
With no argument, this uploads ~/.ssh/id_ed25519.pub (falling back to id_ecdsa.pub, then id_rsa.pub). To upload a specific key:
paas keys add ~/.ssh/other_key.pub
paas keys list shows your keys with their IDs and fingerprints; paas keys remove <key-id> deletes one.
Add the paas remote
From inside your app’s local git repository, run:
paas git remote -a web
This looks up the app’s platform-hosted repository, adds it to your local repo as a remote named paas (or updates the remote if it already exists), and prints the result:
$ paas git remote -a web
Set remote paas → ssh://git@<git-host>:<port>/sources/acme/shop/production/web.git
The exact host and port are filled in for your installation. To print the URL without touching your git configuration, add --print. Running the command outside a git repository also just prints the URL.
-a matters. Bare paas git remote — no -a — sets up the Environment’s shared remote instead, named d2o (see One remote for several apps below). For a single app’s own remote, always pass -a <app>.
If an older checkout already has a d2o remote left over from before the shared remote existed — pointing at this app’s own repository, not an Environment — paas git remote refuses to touch it rather than silently repointing it to the Environment (a push there would then build nothing). This applies both to the bare command and to -a <shared-app> for an app on the shared source. Fix it first, then re-run whichever command you meant:
git remote rename d2o paas
If paas is already taken too (both remotes present in the same checkout), rename isn’t possible — remove d2o instead, since the per-app remote it pointed at is already available as paas:
git remote remove d2o
Push
git push paas main
The platform builds the main branch, so push to main. If your local branch has a different name, push it as main:
git push paas HEAD:main
The push returns within seconds, printing a deployment number, the commit, a link to follow it live, and the paas watch command to run:
remote: Deploy #42 started for abc1234 → web
remote: Follow live: https://d2o.io/orgs/acme/projects/shop/envs/production/deploys/42
remote: Or run: paas watch --deploy 42
It doesn’t wait for the build — open the link or run the printed command to follow what happens next.
When a push is refused
A push is refused, and nothing is built, in two cases:
- The Environment is promote-only. It runs versions promoted from another Environment, so the push is refused with
environment production is promote-only; push to `staging` and promote. Push to the named Environment instead and promote from there. The dashboard shows no push command for a promote-only Environment. - The app or Environment deploys from a linked GitHub or GitLab branch and direct pushes are off. The message says where to push instead:
This environment deploys from GitHub acme/shop@main. Push there instead, or allow direct pushes in the environment's settings.The dashboard hides the push command and says Direct pushes are off. See Deploy from GitHub or GitLab.
What happens next
- Build. The platform builds a runnable version from the pushed source with buildpacks, using the language/framework you chose when creating the app.
- Deploy. When the Environment has Auto Deploy enabled (the default), a successful build rolls out automatically, and only takes traffic once it passes its health check. With Auto Deploy off, the build waits until you deploy it yourself with
paas deploy -a web. See Deploys, Health Checks & Rollback for the full stage-by-stage picture, including what happens when a new version never becomes healthy.
Without --deploy, paas watch follows the newest deploy for the app:
paas watch -a web
Or check on it yourself:
paas releases # recent deployments for the environment
paas status -a web # the app's sync, health and instances
paas logs -a web --tail
While the image is still building, the log stream tells you so instead of streaming; once the new instances are running, logs flow normally. In the dashboard, the app’s Deployments view shows the same history and status.
HTTPS remote alternative
If you can’t use SSH (for example, from a machine with no SSH key set up), push over HTTPS with a URL that embeds an access token scoped to this app’s repository. The token expires after 30 days, and until then it grants push access, so treat the URL like a password.
In the dashboard: on the app’s Deployments tab, the Deploy via Git Push panel has a Show push command button. It generates ready-to-copy commands:
git remote add paas https://x-access-token:<token>@<git-host>/sources/acme/shop/production/web.git
git push paas HEAD:main
Use Regenerate token to mint a fresh URL for your next push. Don’t share the URL or commit it anywhere — it carries write access to the app’s source repository.
With the CLI:
paas git remote -a web --https
This configures the paas remote over HTTPS instead of SSH and stores a freshly minted push token with git’s credential helper, so the token never appears in .git/config or git remote -v. It needs a credential helper (your OS keychain, or git config --global credential.helper store) and stops with an error when none is configured. You can run it again at any time for a fresh token. When a push fails with fatal: Authentication failed after the token’s 30 days are up, run the same command again.
For CI pipelines, create a deploy token for the app’s remote (paas deploy-tokens create ci -a web), or for the Environment’s remote when your apps use the shared repository — see Deploying from CI.
One remote for several apps
Apps in one Environment can share one repository: the Environment’s own, at
sources/<org>/<project>/<environment>.git. One push then rebuilds every app
that uses it, and apps whose code is the same — same directory, same
framework — share a single build, so the image is built once and deployed to
all of them.
Create each app with --shared-source, and give each app its process with
--process when one build produces several (for example web and worker
from a Procfile, or two binaries of one Cargo project):
paas apps create admin --shared-source --framework rust --port 3000 --process admin -e production
paas apps create bot --worker --shared-source --framework rust --process bot -e production
--process is only needed when the app’s name doesn’t already match a line
in the Procfile — a newly created app named worker with a worker:
Procfile line picks it up automatically, with nothing to set. Name matching
only applies to apps created from now on, though: an app that already existed
keeps running its current process regardless of its name, until you clear its
Process setting (paas apps update <app> --process ""). See
Procfile for the full order the platform tries. A
file’s own d2o-paas.yaml can set process: too, for apps that keep their
settings in the repository — see
d2o-paas.yaml Reference.
If an app’s code lives in a directory of the repository, add
--source-dir <DIR>. Then add the Environment’s remote — without -a — and
push once:
paas git remote -e production
git push d2o main
paas git remote without -a adds the Environment’s remote as d2o; an
app’s own remote keeps the name paas. In the dashboard, the Environment’s
Deployments tab has a Git remote panel with the same commands.
Inside the shared repository the CLI works out the app from your working
directory: the app whose directory contains it, or the only app that builds
from the repository root. When several apps build from the root, pass -a.
paas watch without -a follows every app of the push until each runs the
new build.
To move an existing app to the shared repository:
paas apps update admin --shared-source -e production
The app keeps running its current version until the first shared build
succeeds. Its own repository is kept until you delete the app, but it no longer
accepts pushes; switch back with paas apps update admin --own-source.
Switching back rebuilds immediately from the app’s own repository’s last pushed commit — whatever that was before you moved it to the shared repository, which can be old or stale. The own repository starts accepting pushes again the moment you switch back, so push the code you actually want right away rather than assuming the version that comes up is current.
Next steps
- How Builds & Deployments Work — what happens between push and running app
- Deployments, Health Checks & Rollback — stages, health checks, and rollback
- Logs — snapshots and live streaming
- Account, SSH Keys & API Tokens — managing your keys and credentials