Building
The three ways to deploy, how environment variables and secrets work, and what happens when a deploy goes wrong.
A deploy takes something you already have — an image, a repository, or a name from the app catalog — and runs it as a container on one of your servers. The command is the same in all three cases; only the argument changes.
Pass an image reference. Anything with a registry hostname (ghcr.io/..., quay.io/...) or a tag (redis:7) is treated as an image and pulled directly. Nothing is built.
clikdeploy deploy nginx:alpine
Pass a GitHub URL and the platform clones it, builds an image from it, and runs the result. Use -b to pick a branch; a /tree/<branch> URL works too.
clikdeploy deploy https://github.com/owner/repo -b main
Pass a bare name. It is looked up in the app catalog first, and if it is not there, searched on Docker Hub. The catalog is a list of known-good pull names — there is no separate install path, so a catalog app deploys exactly like any other image.
clikdeploy templates list clikdeploy deploy ghost
If you have more than one server connected, add --server with the server name or ID. Without it the CLI stops and lists your servers rather than guessing.
clikdeploy deploy ghost --server prod-1
Environment variables are the values your container reads at startup. You set them per app, and they are applied on the next deploy.
clikdeploy app env list my-app clikdeploy app env set my-app LOG_LEVEL debug clikdeploy app env set my-app API_KEY sk-live-xxxx --secret clikdeploy app env delete my-app LOG_LEVEL
--secret masks the value everywhere it would otherwise be printed — the CLI, the dashboard, and logs. It does not change how the value reaches your container.
Every value you store is encrypted before it reaches the database, along with your SSH keys, registry credentials and Git tokens. Encryption is AES-256 with a key derived from the platform's encryption key and salt. Values are decrypted at deploy time, to be injected into the container, and nowhere else.
Most apps need values nobody wants to invent by hand: a database password, a connection string, a session secret. The platform generates those — internally this is called clikvars. The rule from the outside is: if your app needs a value and you did not set one, a correct value is generated and injected.
SECRET_KEY_BASE (Rails), APP_KEY (Laravel), SECRET_KEY (Django), NEXTAUTH_SECRET (Next.js).*_PASSWORD, *_TOKEN or ADMIN_* key that the image itself asks for, so the app does not boot on a published default.Third-party credentials are never invented. A Stripe key, a GitHub token or an OAuth client secret has no value the platform could correctly guess, so those keys are left for you to set.
A value you set always wins. Generation only ever fills a key that has no value yet — it never overwrites one.
| Source | Wins over |
|---|---|
You | Everything below. Set once, it stays set. |
Catalog / compose | Values that came with the image or its compose file. |
Generated | Only applied to keys still empty after the two above. |
Setting a value turns off the safety net for that key
DATABASE_URL you set by hand is used exactly as written — which is what you want when it points at your own external database, and is a hard-to-spot outage when it has a typo in it. If an app cannot reach a database it should be able to reach, check whether you set the connection variable yourself before looking anywhere else.A backing service is something your app talks to but is not — Postgres, MySQL, MongoDB, Redis. When your app needs one, the platform runs it alongside your app on the same server and writes the connection details into your app's environment.
You do not have to name the variables. The wiring reads the values, not the keys: any variable whose value looks like a connection URL for a provisioned engine (postgresql://, mysql://, mongodb://) is pointed at the real service with the real credentials — whatever it happens to be called in your app. So a NEXT_PRIVATE_DATABASE_URL is wired as readily as a DATABASE_URL.
A URL that points at a real external host — a hostname or IP that is not part of this stack — is treated as your own database and is never rewritten.
clikdeploy app database status my-app
Nothing is thrown away. A failed deploy's containers, volumes and networks are kept exactly as they were, so you can look at them and retry. They are only cleaned up after about a week of no activity — no retry, no opening the app — and you are warned a day before that happens.
Before a deploy is reported as failed, the engine reads the container's logs and state, classifies what went wrong, applies a fix, and re-checks health. These are deterministic repairs, not guesses:
--install step), so that command is run, once.mongod does not provide, so rs0 is initiated and the connection URL is patched.Those retries happen inside one deploy. The job queue itself does not replay a failed deploy behind your back — if the deploy is reported failed, it is finished, and the next attempt is yours to start.
Start with diagnose. It correlates the deploy status, the stage that failed, the error lines and the likely next action in one place.
# start here clikdeploy diagnose my-app # your container's own output, live clikdeploy app logs my-app -f clikdeploy app logs my-app -n 500 # the build and deploy stages for one deployment clikdeploy deployment logs <deploymentId> clikdeploy deployment diagnose <deploymentId> # everything that happened to this app, in order clikdeploy timeline app my-app --since 6h
clikdeploy app logs is your container. clikdeploy deployment logs is the platform building and starting it. When an app builds fine but will not stay up, the answer is almost always in the first.
# run the same deploy again clikdeploy app retry my-app # see what you can roll back to, then do it clikdeploy app rollback my-app --list-targets clikdeploy app rollback my-app --target <deploymentId>
A backup captures your app's configuration and a dump of its database, if it has one. Automatic backups run at most once per app per day. Restoring overwrites the current database with the dump, so it is not a step to take casually.
clikdeploy app backup create my-app --label before-upgrade clikdeploy app backup list my-app clikdeploy app backup restore my-app <backupId>
How long backups are kept depends on your plan: 7 days on Free, 30 on Pro, 90 on Team, 365 on Enterprise.
Next: domains and uptime to put a real URL on the app you just deployed.