DocsApplications
Creating an application
Connect a Git repo or a ready-made image to a project and control how its container is run.
- Time
- ± 3 minutes + build duration
- Needs
- A local registry (Git source) or an image
- Result
- A running container from the branch you chose
Prerequisites
- For applications from a Git repo: the local registry is already provisioned — built images get pushed there. Without it, the first deploy fails.
- You already have a project for this application to live in.
- For private repos: a Git credential has already been added in Settings.
- The application listens on a fixed port and accepts connections from
0.0.0.0, not justlocalhost.
Two application sources
| Source | What you fill in | Deploy flow |
|---|---|---|
| Git repository | Repo URL, branch, credential (if private), build method | Clone → build (Dockerfile / Nixpacks / static) → push to registry → run. |
| Ready-made image (pulled) | An image reference (ghcr.io/acme/api:1.4) and, if private, the external registry holding its credentials | No build: the image is pulled then run. Supports automatic updates when the tag's digest changes. |
The local registry is only required for a Git source (where built images are stored). Applications from an image don't need it.
Steps
On the project page, click New application
Fill in the form. Most of it can be changed later in the Settings tab.
Field Description Name Becomes a unique slug (app_name); container name = aoox-app-<slug>, shown (with a copy button) in the application header; other apps on the same server reach it at http://aoox-app-<slug>:<container port>. Git repository The repo's https URL. Without a user:token in the URL — use the Git credential select for private repos. Branch The branch that gets built. The webhook only reacts to pushes on this branch. Git credential None (public repo) or one of your stored credentials. Build method Dockerfile or Nixpacks — see How builds work. Container port The port the application listens on inside the container (e.g. 3000). Host port Optional. Fill it in for IP:port access without a domain. Leave it empty when using a domain — blue/green requires an empty host port. Listens on Only with a host port. Public (default for applications) publishes it on every interface; This server only (127.0.0.1) makes it reachable from the server itself or an SSH tunnel. Docker-published ports bypass ufw, so use the provider firewall for public ones (see Start from a fresh VPS). Changing it recreates a running application like a new host port does. Not available for Swarm services. Health check path Optional but recommended, e.g. /health. Determines deploy success/failure and enables blue/green.Server The aoox host (default) or a remote server. Only shown if a server is registered. Deploy mode container (default) or service with replicas when Swarm is active — plus node placement and rolling-update settings. Auto update Image sources only: periodically checks the tag's digest and deploys when it changes. CPU / memory limits Optional. See Monitoring. Example — a Next.js application in a public repo- Name
- shop
- Git repository
- https://github.com/acme/shop
- Branch
- main
- Git credential
- None (public repo)
- Build method
- Nixpacks
- Container port
- 3000
- Host port
- (empty — using a domain)
- Health check path
- /health
Choose Access
Before saving, the form asks how this application will be reachable — so a freshly created application doesn't silently end up unreachable once the deploy finishes.
Option What happens IP + port A host port is assigned automatically (checked for conflicts against other applications/databases/compose stacks/containers before saving) — immediately reachable at http://<server-ip>:<port>.Domain A host + HTTPS toggle is added right now, instead of later from the Domain tab. If the reverse proxy hasn't been provisioned yet, it's started automatically — the same behavior as setting the panel domain, see Domain for the panel → Via the dashboard. Set up later No port or domain yet. The application still gets deployed, but isn't reachable until one is added later from the Settings or Domain tab. Save, then fill in environment variables (if needed)
The Environment tab → env editor, one
KEY=VALUEper line. Values are applied when the container is created, not at build time — see Environment variables.Open the Deploy tab and click Deploy
What happens, visible in the realtime log:
queued deployment created building Docker daemon clones the repo → builds the image pushing image → local registry (skipped on a remote server) starting new container created, waiting for healthy success old container replaced · application status RunningThe next deploy is rejected (409) while a deployment is still active. A failed build/push doesn't touch the running container. Details in Deploy & rollback.
Verification
- The first deployment ends in
successand the application's status badge reads Running. - The container log (Deploy tab) shows the application's output, e.g.
Listening on :3000. - If using a host port:
curl http://<server-ip>:<host-port>/responds. If using a domain: continue to Proxy & domain.
Tabs on the application page
| Tab | Contains |
|---|---|
| Deploy | Deploy/Stop/Start buttons, deployment list, deployment & streaming container logs, metrics, Rollback. |
| Settings | The same form as creation, plus build args (environment has its own tab). Host port, domains, and resource limits apply immediately to a running application — the container is recreated from the same image without a build (with a host port there is a brief downtime, since two containers cannot bind the same port). A stopped application picks up its new port when started again, and a host port change is rejected (409) while a deployment is active. Other settings take effect on the next deploy. Also holds the Maintenance mode card. |
| Domain | Hostname for the application + HTTPS toggle + DNS check. |
| Mount | Volumes, binds, files — and volume backups. |
| Environment | The application's env variables, saved on their own, plus the secret source (Infisical). Also the Variable sets card: the ordered set environments this application links, which ${{vars.KEY}} resolves from (see Variables). Applies on the next deploy. |
| Jobs | Scheduled commands. |
| Webhook | Webhook URL, secret, and pull request previews. |
| Console | Interactive shell straight into the running container. |
Console
The Console tab opens a docker exec -it into the application's running container, right from the dashboard — different from the Terminal page, which opens a shell on the host over SSH. Handy for inspecting files, running a debug command, or checking the environment without needing SSH access to the server.
- Access: project role developer and up (including platform admin/owner) — a viewer never sees this tab, and the API rejects it too if tried directly. A read-only API token is rejected as well.
- Logged in the audit log — every console session opened is its own row in Settings → Audit log, with who and when.
- service mode (Swarm) with more than one replica: pick a task from the dropdown above the terminal; only tasks running on this node are reachable.
- Shell used:
bashif the image has it, otherwisesh. A distroless/shell-less image shows Docker's own error message and the session ends right away — not a bug, there simply is no shell to open. - Changes made in this shell are temporary: files edited through Console are lost on the next redeploy, unless they live on a mounted volume.
Log error detection
Every minute, the API scans the container log of every Running application for common error patterns (traceback, panic, exception, level=error, and similar) and sends an Application error notification when it finds one — see Notifications for the pattern list, cooldown, and how to turn it on (off by default, per channel).
For an application whose normal output just happens to look like errors (e.g. a verbose framework), turn on Ignore error logs on the Settings tab — that application is skipped from the scan entirely, not just muted.
Maintenance mode
Show visitors a maintenance page instead of the application, without stopping it. It is the first card on the Settings tab, called Maintenance mode.
- The Enabled switch applies immediately and does not restart or recreate the container: only Traefik's routing changes. While it is on, the status strip shows a Maintenance badge.
- Every request to every domain of the application gets the page with HTTP
503andRetry-After: 300(5 minutes), so search engines and monitors see a temporary outage. This includes HTTPS domains with a custom certificate, and your own middlewares still run in front of it. - Page message: plain text, up to 280 characters, with its own Save message button; leave it empty for the default text. It is always shown as text, never as HTML. The page itself is in English.
- A deploy while it is on keeps the page up. A domain you add or remove while it is on is covered or released automatically. Switching it off brings the application back at once.
- Developers and up can switch it; viewers see the card read-only.
IP:port): only the domains are put in maintenance.- Applications on a remote server are not supported yet: the card says so and the API refuses it (400).
- You cannot move an application to another server while it is in maintenance (409); switch it off first.
- Project export/import does not carry the flag, so an imported application never starts in maintenance.
Common pitfalls
| Symptom | Cause & fix |
|---|---|
| Monorepo: build cannot find package.json / Dockerfile | Set Root directory (for example apps/web) in the Git part. See Build types. |
| Deploy fails at the pushing stage | The local registry isn't provisioned yet, or REGISTRY_PUBLIC_HOST isn't localhost without TLS. |
| Build fails: repository not found | A private repo without a Git credential, or the token lacks read access. |
| Pull fails: unauthorized (image source) | A private image with no external registry selected, or an expired registry token. |
| Health check never turns healthy | The container port doesn't match the port the app listens on, the app only listens on 127.0.0.1, the path doesn't return 2xx, or the image lacks wget/curl/node/python3. |
| Running but the domain 404s | The container isn't healthy yet, or DNS isn't pointed at it yet — use the DNS check button on the Domain tab. |
| New env doesn't take effect | Env is applied when the container is created — deploy (or rollback) once more. |
Next steps
Found a mistake? Edit this page on GitHub ↗