docs Applications
Applications
Create, configure, deploy, scale, and operate applications built from Git or pulled from a registry.
Source Types
Create an application from your project page with New Application. The Source field accepts:
GitHub
Pick a repository through the GitHub App (when one is configured), or use a repository URL with an optional access token for private repos.
GitLab
Repository URL with an optional GitLab access token for private repos.
Git (any)
Any Git repository reachable over HTTPS, with optional token authentication.
Docker Image
Deploy an existing image from a registry. Nothing is built — the image is pulled on every deploy.
When an application is connected through the GitHub App, it is cloned with a short-lived installation credential (no token is stored for the app), pushes and pull requests arrive without a per-repo webhook, and DeployKit can report commit statuses back to GitHub. You can switch an existing app between the GitHub App and a URL + token from the General tab (Change repository / Connect through the GitHub App instead). Without the App, copy the webhook URL shown in the General tab into your repository's webhook settings to auto-deploy on push.
For a Docker Image source, the image reference (for example ghcr.io/acme/api:1.4.0) is stored in the Repository URL field of the General tab.
Port and Networking
Set Port to the port your process listens on inside the container (default 3000). With at least one domain, Traefik routes traffic to that port and nothing is published on the host. Without a domain, the same port is published on the host (host:port → container:port), so two apps without domains cannot share a port. Domains are managed from the Domains tab — an app can have several; see Domains & SSL.
Tip: your app must listen on the configured port and bind to 0.0.0.0, not localhost. A process bound to localhost starts fine but is unreachable from Traefik.
Build Strategies
Nixpacks (auto-detect, default)
Detects your language and framework and builds an image with no configuration. Override the detected start command with Start Command (Nixpacks only). If Nixpacks fails or is not installed, the build automatically falls back to the Dockerfile strategy. A Nixpacks build times out after 10 minutes.
Dockerfile
Uses your own Dockerfile. Set Dockerfile Path relative to the repo root (or to the Root Directory), without a ./ prefix; the path must stay inside the build context. If no Dockerfile exists, DeployKit generates one for common stacks: Go, Python, Next.js, static SPAs (Vite, CRA, Vue, Angular, Svelte) and Node servers. For Node servers it runs your start script, so multi-step commands like prisma migrate deploy && node dist/index.js run in full.
Buildpacks
Cloud Native Buildpacks via pack build. Good for projects that follow standard conventions.
Remote servers: apps deployed to a remote server are cloned and built on that server with docker build using the repository's Dockerfile. Nixpacks, Buildpacks and Dockerfile auto-generation only run for apps on the local server, so make sure the repo ships a Dockerfile when targeting a remote server.
Monorepo Support
Deploy applications from subdirectories by setting the Root Directory (for example apps/web). DeployKit uses that directory as the build context. When it generates a Dockerfile for a workspace member, it detects the package manager (lock files, then pnpm-workspace.yaml or the packageManager field) and builds the package together with its workspace dependencies.
Environment Variables
Manage environment variables from the Environment tab of your application, in KEY=VALUE format, one per line. Values are masked until you click Reveal. All variables are encrypted at rest using AES-256-GCM and injected into the container at runtime. Changes take effect on the next deploy.
For security, environment variables are not passed as Docker build arguments — build args leak into image layers and are readable via docker inspect. Instead, before each build DeployKit writes your app's variables to a .env file at the root of the build context, so frameworks that inline variables at build time (Vite, Create React App, Next.js) can pick them up.
- Nixpacks / Buildpacks — the generated .env sits at the build context root, where most frameworks read it automatically. No extra setup needed.
- Custom Dockerfile — your Dockerfile must COPY the generated .env into the build stage before the build step runs; otherwise build-time variables are silently missing.
- Monorepos — the .env is written at the context root (the repo root when Root Directory is empty). Copy it into the app subdirectory: frameworks like Vite read env files from the app directory, not the monorepo root.
- Check your .dockerignore — if it excludes .env, the Docker daemon drops the generated file from the build context and build-time variables never arrive.
Example for a Vite app in a pnpm monorepo (build context = repo root):
# DeployKit writes your app's env vars to a .env at the context root.
# The ".env*" glob keeps local builds working when no .env exists.
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .env* ./
COPY apps/web apps/web
# Vite reads env files from the app directory, not the monorepo root
RUN if [ -f .env ]; then cp .env apps/web/.env; fi
RUN pnpm --filter web buildPersistent Volumes
Add mounts under Persistent Volumes in the General tab (up to 20). The source can be an absolute host path or a Docker named volume, and the container path must be absolute. An optional :ro suffix mounts it read-only. Volumes persist across deploys, rollbacks and replicas.
/srv/myapp/uploads:/app/uploads # bind mount from the host
myapp-data:/var/lib/data # Docker named volume
/srv/myapp/config:/app/config:ro # read-only- Bind mounts of sensitive host directories (such as /etc, /proc, /sys, /dev, /boot, /root) and paths containing '..' are rejected.
- Paths that the image declares with a VOLUME directive and that you did not mount are automatically covered by a stable named volume (dk-<app>-<path>), so their data survives redeploys instead of landing in a fresh anonymous volume each time.
- After the container starts, DeployKit inspects its real mounts. If a configured volume is missing, the deploy fails loudly rather than letting writes go to the container filesystem, where the next redeploy would wipe them.
Health Checks
After the container starts, DeployKit can check that your app is actually serving. Choose the type in the General tab:
- HTTP — sends a GET to the configured path (default /) on the app's first domain, or on localhost:<port> when it has no domain. 2xx and 3xx responses count as healthy; redirects are not followed.
- TCP — checks that the port accepts connections. Useful for non-HTTP services.
- None — skips the check; the app is marked running as soon as the container starts.
Configure timeout (default 5 s), interval (default 10 s) and retries (default 6). With Fail deploy if health check does not pass enabled, a failing check marks the deployment as failed; otherwise it only logs a warning and the container keeps running.
Resource Limits
Under Resources, set a CPU (cores) limit (0.1 to 8) and a Memory (MB) limit (64 to 32768) per container. Leave them empty for no limit. Limits apply to every replica and take effect on the next deploy.
Replicas and Autoscaling
Set Replicas (1 to 10) to run several identical containers; Traefik load balances between them. The General tab shows the live state of each instance.
Enable Autoscaling to adjust the replica count by average load across running replicas, within Min replicas and Max replicas. Set a CPU target % (relative to the CPU limit, or to one core if no limit is set) and/or a Memory target %; leave a target empty to ignore that metric. Scaling happens live, without a rebuild.
- Load is evaluated every 30 seconds.
- Scale up: one replica is added after load stays above the target for 1 minute.
- Scale down: one replica is removed only after load stays below half the target for 5 minutes.
- After any scaling action there is a 180-second cooldown.
- Autoscaling currently works for apps on the local server only.
Replicas need a domain. Without a domain the app publishes a fixed host port, which a second container cannot bind, so DeployKit deploys a single instance (the deploy log says Replicas require a domain). The Replicas and Autoscaling fields stay disabled until the app has a domain. If autoscaling "does nothing", check this first.
Maintenance Mode
Click Maintenance in the application header to show a maintenance page on all of the app's domains, with an optional message (up to 500 characters). Your app is not stopped or redeployed: a small maintenance container takes over the app's domains in Traefik, and turning it off restores traffic immediately.
- Every URL returns HTTP 503 with Retry-After, so caches and uptime monitors never treat the page as up.
- The page shows since when the app is in maintenance, adapts to light/dark mode and English/Spanish, and reloads itself once the app is back.
- Maintenance survives deploys. You can deploy, stop or scale the app behind it and check it before reopening; it only ends when you click Turn off.
- Use Edit message to change the text while it is active. Domains added during maintenance are covered too.
- The app needs at least one domain, and you need the operator role on the project. Works on remote servers too.
Logs
Container logs are collected continuously for every running container, whether or not anyone has the dashboard open. The Logs tab has two modes:
- Live — streams the container's output in real time.
- History — searches persisted logs by text and filters by level (error, warn, info, debug, fatal). Levels are inferred from the log text on a best-effort basis.
Persisted logs are kept for 7 days by default (set LOG_RETENTION_DAYS on the API to change it). Common secrets — tokens such as ghp_ or sk_live_, Bearer/Basic headers, tokens in Git URLs, password= pairs and AWS keys — are redacted before logs are stored or streamed. Build output for each deploy is in the Deployments tab.
Web Terminal
The Terminal tab opens an interactive shell inside the running container from your browser (click Connect; fullscreen is available).
- The app must be deployed and running.
- Requires the operator or admin global role and the operator role on the app's project.
- The image must contain /bin/sh, /bin/bash or /bin/ash; images without a shell (e.g. distroless) cannot open a terminal.
- Sessions close after 15 minutes of inactivity.
- Commands are not recorded. For a one-off command with a stored exit code and output, use the Tasks tab instead.
Rollbacks
Every deployment records its commit, build logs and the image it produced. From the Deployments tab you can roll back to a previous deployment. Nothing is rebuilt: DeployKit restarts the app from the stored image, so a rollback takes seconds. It appears as a new deployment named Rollback to <commit>. Only the image changes — the app's current environment variables, domains, volumes, limits and replica count are used. Rolling back requires the operator role on the project.
Rollback depth is 3. A daily cleanup (around 03:00) keeps only the images of each app's 3 most recent successful deployments plus any image in use. Older entries still appear in the list but cannot be rolled back (No image stored — cannot roll back).
Deploying and Day-to-Day Controls
- Deploy — builds and deploys the app's default branch.
- Deploy from branch (the branch button next to Deploy) — deploys any branch once, without changing the app's default branch.
- Stop / Start — stops all of the app's containers or starts them again without a rebuild.
- Auto-deploy — a push to the default branch triggers a deploy through the GitHub App or the repository webhook (see Webhooks).
- Other tabs: Monitoring (CPU/memory metrics), Security (image vulnerability scan results), Tasks (scheduled and one-off commands) and Previews (pull request environments).
Deployment Pipeline
When you trigger a deployment, the following happens:
- A deployment record is created with status 'queued' and the request returns immediately.
- Deploys of the same app run one at a time; a new deploy waits until the previous one finishes.
- The repository is cloned (shallow, chosen branch) using the GitHub App credential or the stored token — or, for Docker Image sources, the image is pulled.
- Env vars are decrypted and written to a .env file at the build context root, so build-time variables reach the build.
- The image is built with the selected strategy and tagged deploykit/<app>:<commit>. On remote servers, clone and build happen on that server.
- Optionally, the image is scanned for vulnerabilities (advisory only — a scan never blocks a deploy).
- DeployKit re-reads the app's settings, so config changes saved during the build (env, domains, volumes, limits) are applied.
- All old containers of the app are stopped and removed, then the new container(s) start with Traefik routing labels, configured volumes and resource limits.
- The real mounts are verified and the health check runs (if configured).
- The deployment status is updated, notifications are sent and, with the GitHub App, a commit status is reported.
- Build and deploy logs stream to the dashboard in real time.