Building apps
The deploy lifecycle
A deploy turns your code into a running app behind a URL. There are two ways to start one — push to a connected GitHub repo, or upload straight from your laptop — and from there every deploy goes through the same steps. This page covers both paths and what happens in between.
Two ways to deploy
Both end the same way: a built, running, health-checked app. They differ only in where the code comes from.
| Path | How it starts | Best for |
|---|---|---|
| GitHub | You connect a repo; a webhook auto-deploys on every push to source.branch. |
The day-to-day flow once an app is set up. |
| Local upload | astrodock deploy --local uploads your working directory. No GitHub. |
Trying things out, no-repo apps, or deploying private code from your machine. |
You can use both
An app can have a connected repo and still take a one-off --local deploy
whenever you want. The manual path always works.
Path A — Connect a GitHub repo
Once a repo is connected, you stop thinking about deploys: every git push to the
configured branch ships automatically.
What you need first
Connecting a repo requires the platform to have a GitHub token so it can read the repo and
create a webhook. That's set once, on the platform, as ASTRODOCK_GITHUB_PAT (in the
platform's .env / dashboard) — it is not an app variable and never goes in
app.json.
Connect it
Name the repo in app.json under source, then run apply:
"source": {
"githubRepo": "your-org/invoice-tool",
"branch": "main"
}
astrodock apply
On apply, Astrodock connects the repo and creates a webhook on it.
From then on, a push to main (or whatever source.branch is) triggers a
deploy on its own. You can also set the repo from the dashboard instead of in the manifest.
Monorepos
If the app lives in a subdirectory, set source.repoPath (e.g. "apps/web").
The deploy builds from there instead of the repo root.
Path B — Deploy from your laptop
No repo, no webhook — just upload what's in the current folder:
astrodock deploy --local
The --local flag packages your working directory and uploads it, then runs the exact
same build/run/health steps as a GitHub deploy. This is what Deploy your
first app uses, and it's the quickest way to iterate before (or instead of) wiring up GitHub.
What a deploy actually does
However it's triggered, every deploy walks the same pipeline. Each step streams into a log you can watch live:
-
Get the source
Clone or pull the connected repo at
source.branch, or unpack the upload from a--localdeploy. IfrepoPathis set, that subdirectory becomes the deploy root. -
Check the required-variable gate
Before any building happens, the platform confirms every required value is set. If anything is missing, the deploy stops right here — nothing is cloned, built, or run. (Details below.)
-
Build
For a Node app:
npm ciplus yourbuildCommandfor the frontend, andnpm installfor the server. For a Docker app:docker buildfrom your Dockerfile. -
Run
Start the app — a Node process for the buildpack, or a container for a Dockerfile app — with all the injected environment variables in place.
-
Health probe
The platform hits your
GET /health. A 200 marks the deploy healthy; otherwise it's recorded as failed. -
Record & log
The deployment is recorded as success or failed, with the full streamed log kept so you can read it after the fact.
Watching a deploy
From the CLI, add :watch to stream the log to your terminal until the deploy finishes:
astrodock deploy:watch # GitHub-connected app
astrodock deploy:watch --local # upload + watch in one go
Uploading 12 KB for "starter"…
Local deploy triggered for "starter" (deployment …)
[…] Installing server deps…
[…] Building frontend (npm run build)…
[…] Health probe: app is responding
[…] Deploy complete
✓ deploy success
You can also watch from the dashboard: the Deploys tab lists every deployment and its status, and the Logs tab shows the streamed output. After a deploy, your app's runtime logs are also available with:
astrodock logs --lines 200
The required-variable gate
A deploy is blocked until every value the app needs is set. The gate runs before any clone or build, so a misconfigured app fails fast and clearly instead of half-deploying. It checks two things:
- Every
env[]entry marked"required": truethat has no value and nodefault. - Every credential made required by an
externalresource mode — that'sASTRODOCK_DATABASE_URLfor an external database, and theASTRODOCK_STORAGE_*credentials for external storage.
If anything is missing, the deploy is rejected with a list of exactly what to set. Set the values and try again:
# the CLI reads the value from stdin so it never lands in your shell history
astrodock set-secret OPENAI_API_KEY
astrodock set-secret ASTRODOCK_DATABASE_URL
Why a gate at all?
It guarantees an app never starts up missing a database URL or an API key it declared it needs — you get a clear error before the deploy, not a crash loop after it.
Redeploying & triggering manually
For a GitHub-connected app, the normal way to redeploy is to push to the watched branch. To kick off a deploy by hand at any time:
astrodock deploy # deploy the app configured in this folder
astrodock deploy my-slug # deploy a specific app by slug
astrodock deploy --local # upload the working dir and deploy that
You can also trigger a deploy from the dashboard's Deploys tab. Each run is its own recorded deployment, so you can always see what shipped and when.
Next steps
- Build the app you're deploying: App structure & app.json
- Deploy a container instead: Dockerfile apps
- Every variable the gate checks: app.json & env reference