Releases: joaomnuno/coolship
Release list
v0.4.0-rc.3
Pre-release v0.4.0-rc.3. Changes since the last release, from the [Unreleased] section of CHANGELOG.md:
Added
coolship loginin a terminal asked one step at a time and showed its progress as a checklist drawn likedeploy's stages: Self-hosted or Coolify Cloud (which usedhttps://app.coolify.iowithout asking for a URL), the URL, a check of the instance, a context name, the token, a check of the token, and the save. Contexts already saved were listed under the first question but could not be picked or edited there. The answers so far stayed listed above the question; Shift+Tab went back to the previous answer with what was typed kept, as did the up arrow in an empty field or at the top of a list, and Esc or Ctrl-C left without writing anything, exiting 130. The token row showed only the last four characters. The form also ran with--format json, which printed only the result on stdout, and with--token-stdinwhen stdin was a terminal, asking for the token without echo.loginchecked the URL as soon as it was entered, through Coolify's public health checkGET /api/health, before any token was asked for. A URL where something other than Coolify answered failed with the new codenot_coolify; host, connection, TLS, and redirect failures kept their existing codes.- A failed check in the
loginform printed the same code, hint, and docs link the command would print on exit, and offered Retry, Go back, and Leave. A failure in Coolify's own setup (api_disabled,ip_not_allowed,missing_permissions,token_exceeds_role) also offered to open the Coolify page that fixes it in a browser. - When the URL was already saved, the
loginform offered to replace that context's token, save under a new name, or leave. A URL and token that a saved context already held were refused with the new codealready_saved, interactively and with--token-stdinalike, exiting 2, unless--defaultnamed that context while another was the default, which made it the default. coolship alias [NAME]added a short name for Coolship,csby default, next to thecoolshipbinary: a symlink, or a copy on Windows. It refused when the name already ran another command onPATH(naming it) or when the binary's directory was not writable (suggestingsudoor moving the binary), and changed nothing when the alias already existed. An existing file of that name was replaced, andcoolship alias --removedeleted it, only when it was a link to Coolship, or a copy on Windows; a separate Coolship binary of that name, such as the installedcoolshipnext to a development build, was left in place and named. It is listed under Configure in the help.- A command that failed in a terminal because of something Coolship could set right offered the fix below the error,
Set it up now? [Y/n]:coolship loginwhen there were no credentials, logging in to the same context again when the token was rejected (unauthorized), a choice ofcoolship link,coolship init, or leaving when the directory was not linked, a choice among the saved contexts (for this run), a login, or leaving when the context was unknown or none was the default, and opening the instance's Settings, Advanced page (API disabled, IP not allowed) or Keys & Tokens, API tokens page followed by a login (missing permissions, token above the role) in a browser. After the fix, a read ran again at once with the same arguments;deploy,preview,cancel,start,stop,restart,env push, anddomain setaskedContinue with coolship X? [Y/n]first, and so did any command after a page was opened. Declining kept the original error, its docs link, and its exit code. A second failure was reported without another offer. Whencoolship initwas the fix fordeployor another command, it asked noDeploy now?and gave no deploy hint, since the command ran again right after, so onedeployqueued one deployment. No fix was offered with--format json, off a terminal, in CI, withhints = false, whileCOOLSHIP_URLorCOOLSHIP_TOKENwas set (for logins), or forlogin,logout,unlink,config, andalias. initaskedDeploy now? [y/N]in a terminal after creating and linking an application without--deploy, and rancoolship deployfor the same target ony. Enter, or end of input, answered no.hints = falseturned the question off.- The installer ended with the Coolship wordmark, the installed version and path,
Next: coolship login, andOptional: coolship alias (adds cs)when stdout was a terminal.NO_COLORremoved the colour. Piped output kept the singleInstalledline, and the new--quietflag printed only that line, any PATH hint, and errors. The script still never asked a question. coolship configin a terminal opened a form. At the top it showed the binding, where credentials come from, the instance, and the preferences file (the credentials and instance in a directory that was not linked too), none of it editable and never a token. Below wereverbosity,build_logs(auto, true, false),color,hints, andupdate_check. Save wrote only the keys that changed topreferences.toml, Discard wrote nothing, and Esc or Ctrl-C left without writing and exited 130. Piped output,--format json, and runs without a terminal printed the configuration exactly as before.coolship config showprinted the full configuration viewconfigused to print, in a terminal too.coolship config get KEYprinted the value in effect, or the default when the file did not set it.coolship config set KEY VALUEwrote one key and, likegh config set, printed nothing on stdout. Both checked the key and value against the list of preferences and exited 2 for an unknown one, suggesting the closest key.setcreated the file and its directory (modes 0600 and 0700) when they were absent, kept every other line and comment, replaced the file in one rename, and wrote nothing when the file was already invalid.config set build_logs autoremoved the key.- A
hintspreference (defaulttrue). Settinghints = falseturned off the next-step hints afterlink,init, an emptydeployments, and a failed deployment, andconfigshowed it ashints off. - Next-step hints in a terminal: after
linkorinit(without--deploy), stderr saidNext: coolship deploy, anddeploymentswith nothing to list saidNo deployments yet. Next: coolship deploy. Hints were dimmed, went to stderr only, and never appeared with--format json, in CI, or when stdin or stderr was not a terminal. - Added an update notice. Once a day, a released build asked GitHub for the latest release (pre-releases ignored) in the background, gave up after two seconds, and never delayed the command. When a newer release existed, it printed one line on stderr after the command's output, at most once a day per release, with the install command to upgrade. The notice was skipped when stderr was not a terminal, in CI, with
--format json, and for development builds.COOLSHIP_NO_UPDATE_NOTIFIER=1or the newupdate_check = falsepreference turned it off, andconfigshowed the preference asupdate check off. The last check was cached instate.jsonnext topreferences.toml. - Added
coolship uias an experimental command, listed under Maintain in the help with a summary starting(experimental). It opened a full-screen menu. The header showed the linked application with its environment, project, and context, its status with a coloured dot, and its last deployment with status, short commit, and how long ago, orunavailablewith the reason when the history could not be read; it was read behind a spinner while the menu was already usable, read again every five seconds, and read at once withr. In a directory that was not linked it saidnot linkedand still offeredlinkandinit. Below it the verbs were listed in the help's five groups and order, subcommands such asenv pushanddomain setincluded. The arrow keys orjandkmoved, typing (or/) filtered, and Enter closed the menu and ran the verb as if typed, with the same global flags, so its normal output showed.preview,domain set,logout,config get, andconfig setwere marked with an ellipsis and asked for their pull request, domains, context, or preference key and value first; Esc there returned to the menu. Esc,q, or Ctrl-C left without running anything. Without a terminal on stdin and stdout, and in CI, it refused with exit code 2 and pointed atcoolship help. The menu is a draft, and its keys and layout may change or it may be removed. - Added a security policy,
SECURITY.md, saying to report a vulnerability privately through the repository's Security tab rather than in a public issue, and that only the latest release line (0.3.x) gets security fixes. - Verbosity:
--verboseand--debug, both long-only since-vstays the version, set how much a run shows for one invocation;COOLSHIP_VERBOSITY=normal|verbose|debugsets it where flags are awkward, such as CI, and theverbositykey of the preferences file sets the default. A flag beats the variable, the variable beats the preference, and--debugbeats--verbose.--verboseprints one line on stderr for every request Coolship sends, with the method, URL, status, time taken, and which retry it was;--debugadds each request's and response's headers and bodies, curl-style, with the token masked to its last four characters and control characters removed. Debug output redacts secrets in bodies: in JSON, variable values (value,real_value) and any field whose name containssecret,password,private_key,token, orapi_keyare shown as[redacted], so the valuesenv set,env push, andenv pullcarry and the private keyinit --create-deploy-keyregisters stay out of a pasted trace, and a body that starts like JSON but cannot be parsed is withheld.COOLSHIP_DEBUG_UNREDACTED=1sh...
v0.4.0-rc.2
Pre-release v0.4.0-rc.2. Changes since the last release, from the [Unreleased] section of CHANGELOG.md:
Added
coolship loginin a terminal asked one step at a time and showed its progress as a checklist drawn likedeploy's stages: Self-hosted or Coolify Cloud (which usedhttps://app.coolify.iowithout asking for a URL), the URL, a check of the instance, a context name, the token, a check of the token, and the save. Contexts already saved were listed under the first question but could not be picked or edited there. The answers so far stayed listed above the question; Shift+Tab went back to the previous answer with what was typed kept, as did the up arrow in an empty field or at the top of a list, and Esc or Ctrl-C left without writing anything, exiting 130. The token row showed only the last four characters. The form also ran with--format json, which printed only the result on stdout, and with--token-stdinwhen stdin was a terminal, asking for the token without echo.loginchecked the URL as soon as it was entered, through Coolify's public health checkGET /api/health, before any token was asked for. A URL where something other than Coolify answered failed with the new codenot_coolify; host, connection, TLS, and redirect failures kept their existing codes.- A failed check in the
loginform printed the same code, hint, and docs link the command would print on exit, and offered Retry, Go back, and Leave. A failure in Coolify's own setup (api_disabled,ip_not_allowed,missing_permissions,token_exceeds_role) also offered to open the Coolify page that fixes it in a browser. - When the URL was already saved, the
loginform offered to replace that context's token, save under a new name, or leave. A URL and token that a saved context already held were refused with the new codealready_saved, interactively and with--token-stdinalike, exiting 2, unless--defaultnamed that context while another was the default, which made it the default. coolship alias [NAME]added a short name for Coolship,csby default, next to thecoolshipbinary: a symlink, or a copy on Windows. It refused when the name already ran another command onPATH(naming it) or when the binary's directory was not writable (suggestingsudoor moving the binary), and changed nothing when the alias already existed. An existing file of that name was replaced, andcoolship alias --removedeleted it, only when it was a link to Coolship, or a copy on Windows; a separate Coolship binary of that name, such as the installedcoolshipnext to a development build, was left in place and named. It is listed under Configure in the help.- A command that failed in a terminal because of something Coolship could set right offered the fix below the error,
Set it up now? [Y/n]:coolship loginwhen there were no credentials, logging in to the same context again when the token was rejected (unauthorized), a choice ofcoolship link,coolship init, or leaving when the directory was not linked, a choice among the saved contexts (for this run), a login, or leaving when the context was unknown or none was the default, and opening the instance's Settings, Advanced page (API disabled, IP not allowed) or Keys & Tokens, API tokens page followed by a login (missing permissions, token above the role) in a browser. After the fix, a read ran again at once with the same arguments;deploy,preview,cancel,start,stop,restart,env push, anddomain setaskedContinue with coolship X? [Y/n]first, and so did any command after a page was opened. Declining kept the original error, its docs link, and its exit code. A second failure was reported without another offer. Whencoolship initwas the fix fordeployor another command, it asked noDeploy now?and gave no deploy hint, since the command ran again right after, so onedeployqueued one deployment. No fix was offered with--format json, off a terminal, in CI, withhints = false, whileCOOLSHIP_URLorCOOLSHIP_TOKENwas set (for logins), or forlogin,logout,unlink,config, andalias. initaskedDeploy now? [y/N]in a terminal after creating and linking an application without--deploy, and rancoolship deployfor the same target ony. Enter, or end of input, answered no.hints = falseturned the question off.- The installer ended with the Coolship wordmark, the installed version and path,
Next: coolship login, andOptional: coolship alias (adds cs)when stdout was a terminal.NO_COLORremoved the colour. Piped output kept the singleInstalledline, and the new--quietflag printed only that line, any PATH hint, and errors. The script still never asked a question. coolship configin a terminal opened a form. At the top it showed the binding, where credentials come from, the instance, and the preferences file (the credentials and instance in a directory that was not linked too), none of it editable and never a token. Below wereverbosity,build_logs(auto, true, false),color,hints, andupdate_check. Save wrote only the keys that changed topreferences.toml, Discard wrote nothing, and Esc or Ctrl-C left without writing and exited 130. Piped output,--format json, and runs without a terminal printed the configuration exactly as before.coolship config showprinted the full configuration viewconfigused to print, in a terminal too.coolship config get KEYprinted the value in effect, or the default when the file did not set it.coolship config set KEY VALUEwrote one key and, likegh config set, printed nothing on stdout. Both checked the key and value against the list of preferences and exited 2 for an unknown one, suggesting the closest key.setcreated the file and its directory (modes 0600 and 0700) when they were absent, kept every other line and comment, replaced the file in one rename, and wrote nothing when the file was already invalid.config set build_logs autoremoved the key.- A
hintspreference (defaulttrue). Settinghints = falseturned off the next-step hints afterlink,init, an emptydeployments, and a failed deployment, andconfigshowed it ashints off. - Next-step hints in a terminal: after
linkorinit(without--deploy), stderr saidNext: coolship deploy, anddeploymentswith nothing to list saidNo deployments yet. Next: coolship deploy. Hints were dimmed, went to stderr only, and never appeared with--format json, in CI, or when stdin or stderr was not a terminal. - Added an update notice. Once a day, a released build asked GitHub for the latest release (pre-releases ignored) in the background, gave up after two seconds, and never delayed the command. When a newer release existed, it printed one line on stderr after the command's output, at most once a day per release, with the install command to upgrade. The notice was skipped when stderr was not a terminal, in CI, with
--format json, and for development builds.COOLSHIP_NO_UPDATE_NOTIFIER=1or the newupdate_check = falsepreference turned it off, andconfigshowed the preference asupdate check off. The last check was cached instate.jsonnext topreferences.toml. - Added
coolship uias an experimental command, listed under Maintain in the help with a summary starting(experimental). It opened a full-screen menu. The header showed the linked application with its environment, project, and context, its status with a coloured dot, and its last deployment with status, short commit, and how long ago, orunavailablewith the reason when the history could not be read; it was read behind a spinner while the menu was already usable, read again every five seconds, and read at once withr. In a directory that was not linked it saidnot linkedand still offeredlinkandinit. Below it the verbs were listed in the help's five groups and order, subcommands such asenv pushanddomain setincluded. The arrow keys orjandkmoved, typing (or/) filtered, and Enter closed the menu and ran the verb as if typed, with the same global flags, so its normal output showed.preview,domain set,logout,config get, andconfig setwere marked with an ellipsis and asked for their pull request, domains, context, or preference key and value first; Esc there returned to the menu. Esc,q, or Ctrl-C left without running anything. Without a terminal on stdin and stdout, and in CI, it refused with exit code 2 and pointed atcoolship help. The menu is a draft, and its keys and layout may change or it may be removed. - Added a security policy,
SECURITY.md, saying to report a vulnerability privately through the repository's Security tab rather than in a public issue, and that only the latest release line (0.3.x) gets security fixes. - Verbosity:
--verboseand--debug, both long-only since-vstays the version, set how much a run shows for one invocation;COOLSHIP_VERBOSITY=normal|verbose|debugsets it where flags are awkward, such as CI, and theverbositykey of the preferences file sets the default. A flag beats the variable, the variable beats the preference, and--debugbeats--verbose.--verboseprints one line on stderr for every request Coolship sends, with the method, URL, status, time taken, and which retry it was;--debugadds each request's and response's headers and bodies, curl-style, with the token masked to its last four characters and control characters removed. Debug output redacts secrets in bodies: in JSON, variable values (value,real_value) and any field whose name containssecret,password,private_key,token, orapi_keyare shown as[redacted], so the valuesenv set,env push, andenv pullcarry and the private keyinit --create-deploy-keyregisters stay out of a pasted trace, and a body that starts like JSON but cannot be parsed is withheld.COOLSHIP_DEBUG_UNREDACTED=1sh...
v0.4.0-rc.1
Pre-release v0.4.0-rc.1. Changes since the last release, from the [Unreleased] section of CHANGELOG.md:
Fixed
logsright after a deployment, or against a Compose application with a one-shot service that had exited, could refuse with "application is not running (status running:healthy)": Coolify's own logs endpoint resolves the container by name and can briefly disagree with the aggregated status it reports elsewhere. When that refusal comes with a status that still starts withrunning,logsnow retries for about ten seconds before giving up, and the final refusal says what is actually known: "Coolify reports the application as running:healthy but has no running container to read logs from yet; this happens briefly after a deployment, or when a Compose service has exited. Retry in a moment." Any other status keeps the previous wording.- A deployment whose build Coolify skipped for an image with the same commit left the
buildstage dim, as if still pending, for the whole run. The checklist showed– build skipped (cached image), plain output printedStage build: skipped, and the stage event carried the statusskipped. Only Coolify's own skip line marked a stage skipped; a stage whose markers never arrived, such as when the token could not read the build log, stayed unmarked. - Checklist rows were cut to the terminal's width. On a narrow terminal (about 50 columns) a row that wrapped used to throw off the redraw, leaving blank frames and stray lines.
- The spinner no longer put two spaces between its frame and its label.
- The help groups (Get started, Ship, Run, Configure, Maintain) and each group's own workflow order no longer come from turning off Cobra's command sorting for the whole process.
NewRootCommandused to set the package-globalcobra.EnableCommandSortingand never restore it, so any other Cobra command tree built in the same process — for instance an embedder running Coolship's constructor alongside its own — had its help silently reordered too, with no synchronization if two such trees rendered help at the same time. Coolship's own help output is unchanged. - Fixed a second problem the same change had left behind: Cobra sorts a command's children the first time anything reads them with sorting enabled, and does so for good, so any unguarded read on a Coolship tree — an embedder calling
Commands()directly, or Cobra's own__completeand help-completion paths — permanently alphabetized that tree's own help from then on. Coolship's help now carries its declared order on the tree itself and renders from that, never from Cobra'sCommands(), so it survives any such read and never touchescobra.EnableCommandSortingat all; concurrent use of another Cobra tree needs no synchronization because there is no shared state left to race on. initon an already-linked directory inspected the repository, probed the remote for--source auto, and listed projects on the Coolify instance before refusing; it was changed to refuse before doing any of that.init --dockerfile PATHor--compose-file PATH(or--compose-domain SERVICE=URL) required--build-packto name the matching pack as well; the named file became enough to pick the pack when--build-packis not given.--build-packstill won when it was given, and still conflicted loudly with a refinement that named a different pack. Naming both a--dockerfileand a--compose-file/--compose-domainwith no--build-packwas refused with a message naming the conflict, instead of picking dockerfile silently and failing later with a message about a build pack that was never chosen.- A mistyped command got a suggestion:
coolship stautssaidunknown command "stauts" (did you mean "status"?), and the same went forenv pul,help stauts, andcompletion bsah. The exit code stayed 2. --context NAMEnaming no saved context listed the saved context names, and the closest one when the name looked like a typo, instead of only repeating the name. URLs and tokens were never listed.logoutgave the same message.- A directory that was not linked was told about both ways to fix it:
coolship linkfor an existing Coolify application, orcoolship initto create one. --target(or a target argument) on acoolship.tomlwith a single[project]table was reported as "invalid project configuration", though nothing was wrong with the file. It said the file had one target, so the name did not apply. An unknown target name in a monorepo was no longer called invalid configuration either, and suggested the closest target name. Both still exited 2.- The
deployhelp text broke a sentence across a short line; it was rewrapped.
Changed
- Commands on a binding with pinned UUIDs read the pinned project, environment, and application directly and at the same time, instead of listing every project on the instance and walking down one request at a time.
statusalso read the deployment history at the same time as the application. On the live test instance, a fully pinnedstatusfell from five sequential requests to four concurrent ones. A binding with names only still listed projects and environments, and only its last two reads ran together. Missing, ambiguous, and moved resources were reported with the same messages as before. --verbosestopped repeating a request line every two seconds whiledeploywaited. Identical requests in a row printed one line, and the repeats were summed up (GET …/deployments/abc 200 OK ×11 more (last 45ms)) when a different request came or the command ended. Retries and requests without a response still printed every time, and--debugstill printed every request.linkandinitasked for each choice (context, project, environment, application, server, source, GitHub App, deploy key) with an arrow-key picker when run in a terminal, in place of the numbered list: the arrow keys move, Enter picks, and Esc or Ctrl-C cancels without writing anything. Typing filters a list too long for the terminal, and/starts the filter on a shorter one. Choices show names only; the UUID or another detail appears only when two candidates share a name. Whenlinkruns in a directory that is already linked, the cursor starts on the project, environment, and application the binding names, but only when the selected instance is the binding's own context; relinking with another--contextor throughCOOLSHIP_URLmarks nothing as current, since the same names on another instance are other resources. The picker is erased once it ends, leaving the result to name what was linked. When stdin is not a terminal but input is interactive, the numbered list remains and shows the same names, with(current)after the bound resource. Noninteractive runs, piped output, and--format jsonare unchanged.- The site's home page describes each command in plain language instead of slogans, and says to pass
--format jsonbefore piping a result intojq. Its replay ofcoolship deployanimates the stage checklist again, as the terminal draws it: the spinner turns and the clock ticks on whatever is open, and each stage is ticked off in place. Since the checklist arrived, the replay had shown it already finished. - Human output in a terminal showed names only at normal verbosity.
status,link,init,stop,cancel,deploy,start,restart,preview,doctor, and the stop, restart, and cancel confirmations stopped printing the application and deploy key UUIDs, and the progress lines on stderr readDeployment status: queuedinstead of repeating the deployment UUID. Deployments have no name, so a result names one by the eight-character short ID.--verboseand--debugbrought every UUID back. Piped output and--format jsonwere unchanged at every verbosity. cancelaccepted the short ID thatdeploymentsprints, looking it up among the 25 newest deployments, and refused an ID that matched more than one.- In a terminal,
deploymentstitled its first columnID, showed when each deployment was created as a relative time such as3 min ago(exact at--verbose), and wrote durations the way the stage checklist does (0:24,1:05) instead of24sand1m5s. When the terminal was too narrow for the table, it left out the type, then the commit, then the duration, instead of wrapping.statusshowed the last deployment's time the same relative way. - An application status (
Status:instatusandstop, the application check ofdoctor, the stop progress lines, and the stop and restart confirmations) was drawn in a terminal with a dot in the color of its state: green when running, yellow while starting or restarting, red when exited, degraded, or unhealthy.NO_COLORand--no-colorkept the dot and dropped the color. - The home page's terminal replay stopped scrolling sideways: long lines were wrapped so every command and log line became fully visible. A wrapped line continued two columns in, and the deploy checklist kept its alignment.
- A command's own help (
coolship status --help,coolship help env pull) left the rarely used path flags--cwd,--config, and--coolify-configout of its Global Flags section and pointed tocoolship --help, which still listed every global flag. Every command still accepted them. - A deployment that finished under the stage checklist (
deploy,start,restart,previewin a terminal) ended in one summary line,✓ Deployed <application> to <environment> in 0:23, followed by the URL on stdout. It no longer repeated the Deployment, Application, and Status lines below the checklist. Piped output,--format json,--verbose, and failures were unchanged. - The checklist drew container and cleanup indented under the rolling update they run inside, instead of as siblings with overlapping times.
deploy --no-wait(andstart,restart,preview) named the queued deployment once, in the result on stdout, instead of also printi...
v0.3.0
Coolship now covers an application's whole life from the terminal: create it, deploy it, watch the stages, manage its lifecycle and history, and read its logs, variables, and domains. A documentation site and a landing page came with it.
Added
-
Coolship reads a preferences file, one developer's tastes on one machine, at
~/.config/coolship/preferences.toml($XDG_CONFIG_HOME/coolship/preferences.tomlwhenXDG_CONFIG_HOMEis set to an absolute path;%AppData%\coolship\preferences.tomlon Windows), or at the fileCOOLSHIP_PREFERENCESnames. It acceptsverbosity(normal,verbose, ordebug),build_logs(truestreams build logs,falsecollapses them, absent follows the verbosity), andcolor(auto,always, ornever); the keys are read and checked now and take effect in later versions. The file is never created and never holds credentials, which stay in Coolify CLI's file.configshows aPreferencesline with the path and either(absent)or the keys the file sets, and its JSON carries apreferencesobject withpath,present, and the values. An unknown key, a value outside its list, or a file that cannot be read is a warning printed once at the start of the command, naming the file and the key; the file is ignored and the command runs. -
initcreates every kind of application Coolify builds from a repository. The root is read in the order Coolify's own form uses: a compose file (docker-compose.yaml,docker-compose.yml,compose.yaml, orcompose.yml) makes a Compose application, aDockerfilebuilds itself, anindex.htmlwith nopackage.jsonis served as it is, and anything else goes to Railpack, Coolify's default.--build-packtakesrailpack,nixpacks,static,dockerfile, ordockercompose. Each pack has its own flags, refused before any request when they do not belong to the pack:--install-command,--build-command, and--start-commandoverride what Railpack or Nixpacks detect, and--staticserves their build output with nginx from--publish-dir(default/dist);--publish-diralso applies to the static pack;--dockerfile PATHnames the Dockerfile;--compose-file PATHnames the compose file and--compose-domain SERVICE=URL, repeatable, gives each service its domain, with a warning when none is given. A named file must exist. The plan and the result show the settings, and the JSON plan carries them aspublish_directory,dockerfile,compose_file,compose_domains,install_command,build_command, andstart_command. A Compose application has no port of its own, so--portis refused andplan.portis 0. -
The site's home page is a product landing page. The hero carries the install one-liner with a copy button and a link to the quickstart. A 16:9 frame is reserved for a walkthrough recording and shows a poster until one exists; the
showcaseVideoUrlconstant indocs/lib/site.tsembeds it. A replayable terminal plays a reallink,deploy, andlogssession with play, pause, and replay controls and stays still when the reader prefers reduced motion. Below it come a grid of the four verbs, a five-step first session, a table of the same tasks incoolify-cliand Coolship, tabs of built-in features with real samples, the verified Coolify version, and a footer. The dark theme uses one warm accent on a near-black ground; the light theme stays available through the existing switch. Every docs route, search,llms.txt, and Markdown negotiation is unchanged, and both container images build as before. -
stop,start,restart: manage the linked application's lifecycle.stopshows the application and its environment, asks (or takes--yes), then polls the status until it reportsexitedor--timeout(default 2m) passes, and reports the last status seen; Coolify stops and removes the containers, anddeployorstartbrings them back. Only an application that already reportsexitedis left alone, with a warning; any other status is stopped, as in Coolify's own UI, so a crash loop shown asrestartingordegradedcan be stopped.startandrestartqueue a deployment through Coolify's start and restart actions and observe it exactly likedeploy, build log included, with--no-waitand--timeout;restartasks first or takes--yes. Restart-only deployments skip the build when the commit's image exists, except for Dockerfile and Docker image applications, which Coolify deploys in full. -
deployments: list the linked application's most recent deployments (-n, default 10), newest first, with a short UUID, status, commit, type (deploy, restart, rollback, or a pull request preview), creation time, and duration;--format jsoncarries the full UUIDs, source (api, webhook, manual), and timestamps. Build logs are never included. -
cancel: cancel a queued or in-progress deployment — the one named, checked to belong to the linked application, or the single active one, refusing when there is none or more than one. Asks first or takes--yes; deployments that already ended are refused before any request. -
statusnow adds aLast deploymentline (short UUID, status, commit, time) and alast_deploymentJSON object from the deployment history when it can be read; a history that cannot be read is a warning. Existing lines and fields are unchanged. -
init: create a Coolify application for the current repository from its Git remote, then link it exactly aslinkwould. The remote and branch come from Git, the build pack from the application root (Dockerfileor Nixpacks; Docker Compose is refused), and the plan — repository, source, branch, build pack, port, project, environment, server, name — is confirmed before anything is created, or requires--yes.--projectwith--create-projectcreates a missing project;--servernames the server, otherwise the only usable one is used;--deploysubmits and observes the first deployment. A directory that is already linked is refused. -
initfor private repositories.--source(defaultauto) probes the remote with an anonymousgit ls-remote, run in an empty temporary home with credential helpers, the user's Git configuration, and Git's own environment variables disabled so that no stored login (a credential helper,~/.netrc, anhttp.extraHeader) can answer for it, and given up on after 45 s even when the host never answers: a readable repository is created as public, an unreadable one through one of Coolify's private sources, asked for interactively or named with--source github-appor--source deploy-key. A GitHub App is picked with--github-app NAMEor from the list, and its access to the repository is checked through Coolify before anything is created, as is the branch when the app's listing of them is complete (Coolify relays GitHub's first page of 30; a branch beyond it is left for the first deployment).--source publicskips the probe. A deploy key is picked with--deploy-key NAMEor from the list, never implicitly, and the repository is stored in its SSH form for it.--create-deploy-key NAMEgenerates an Ed25519 key, registers it in Coolify, prints the public half once so it can be added to the repository, and stops; the private half is never printed or written. The plan and the result name the source; the JSON of a public plan is unchanged. -
A documentation site under
docs/, built with Fumadocs (Next.js, MDX) and served as a Node.js server from a Docker image for deploying on Coolify at coolship.itrocas.com: an overview, a get-started path, concepts, a reference page per command derived from--help(includinginit,domain,login, andlogout), guides for CI, monorepos, and environment variables, and the platform limits. Every page is also served as Markdown (/docs/<page>.md, orAccept: text/markdown) withllms.txtandllms-full.txtfor assistants, and pages carry copy-as-Markdown and open-in-Claude/ChatGPT actions. The image is built by vinext (docs/Dockerfile), with anext buildimage (docs/Dockerfile.next) as the fallback; thedocs-siteworkflow builds both images on pushes tomainand pull requests that touchdocs/and smoke-tests their routes. -
Refusals of creation and other mutations now carry the server's explanation (its
messageand field errors) in the error, so a repository Coolify cannot reach or a rejected value is reported as such rather than as a bare status code. Reads and server faults still report the status alone. -
Shell completion:
coolship completion bash|zsh|fish|powershellprints a script for the shell, and the README says where each shell loads it from. An unknown shell name or an extra argument is invalid input (exit 2), like every other command's. -
previewtakes the target positionally, likedeploy:coolship preview api --pr 42. -
login: the global--contextnames the instance being saved when--nameis absent, and a line before the token prompt says where to create the token (Keys & Tokens; read, write, deploy; sensitive read for build logs and secret values). -
helpreaches nested commands:coolship help env pull,coolship help domain set.
Changed
deploy,start,restart, andpreviewshow a deployment as a checklist when run in a terminal: the target (→ fenix-bot,→ production), then the deployment and its stages — build, rolling update, container, cleanup — ticking as Coolify's build log markers arrive, with a spinner and elapsed time on whatever is open,✓and its duration on what finished,✗on what failed, and the stages not reached yet dim; warnings print above the checklist and stay, and the final checklist stays on screen before the result. The build log is collapsed by default and printed in full when the deployment fails, between the checklist and theDeployment page:line, since no command can fetch a past build log.--logsstreams the log live above the checklist and--no-logskeeps it collaps...
v0.3.0-rc.1
Pre-release v0.3.0-rc.1. Changes since the last release, from the [Unreleased] section of CHANGELOG.md:
Added
- The site's home page is a product landing page. The hero carries the install one-liner with a copy button and a link to the quickstart. A 16:9 frame is reserved for a walkthrough recording and shows a poster until one exists; the
showcaseVideoUrlconstant indocs/lib/site.tsembeds it. A replayable terminal plays a reallink,deploy, andlogssession with play, pause, and replay controls and stays still when the reader prefers reduced motion. Below it come a grid of the four verbs, a five-step first session, a table of the same tasks incoolify-cliand Coolship, tabs of built-in features with real samples, the verified Coolify version, and a footer. The dark theme uses one warm accent on a near-black ground; the light theme stays available through the existing switch. Every docs route, search,llms.txt, and Markdown negotiation is unchanged, and both container images build as before. stop,start,restart: manage the linked application's lifecycle.stopshows the application and its environment, asks (or takes--yes), then polls the status until it reportsexitedor--timeout(default 2m) passes, and reports the last status seen; Coolify stops and removes the containers, anddeployorstartbrings them back. Only an application that already reportsexitedis left alone, with a warning; any other status is stopped, as in Coolify's own UI, so a crash loop shown asrestartingordegradedcan be stopped.startandrestartqueue a deployment through Coolify's start and restart actions and observe it exactly likedeploy, build log included, with--no-waitand--timeout;restartasks first or takes--yes. Restart-only deployments skip the build when the commit's image exists, except for Dockerfile and Docker image applications, which Coolify deploys in full.deployments: list the linked application's most recent deployments (-n, default 10), newest first, with a short UUID, status, commit, type (deploy, restart, rollback, or a pull request preview), creation time, and duration;--format jsoncarries the full UUIDs, source (api, webhook, manual), and timestamps. Build logs are never included.cancel: cancel a queued or in-progress deployment — the one named, checked to belong to the linked application, or the single active one, refusing when there is none or more than one. Asks first or takes--yes; deployments that already ended are refused before any request.statusnow adds aLast deploymentline (short UUID, status, commit, time) and alast_deploymentJSON object from the deployment history when it can be read; a history that cannot be read is a warning. Existing lines and fields are unchanged.init: create a Coolify application for the current repository from its Git remote, then link it exactly aslinkwould. The remote and branch come from Git, the build pack from the application root (Dockerfileor Nixpacks; Docker Compose is refused), and the plan — repository, source, branch, build pack, port, project, environment, server, name — is confirmed before anything is created, or requires--yes.--projectwith--create-projectcreates a missing project;--servernames the server, otherwise the only usable one is used;--deploysubmits and observes the first deployment. A directory that is already linked is refused.initfor private repositories.--source(defaultauto) probes the remote with an anonymousgit ls-remote, run in an empty temporary home with credential helpers, the user's Git configuration, and Git's own environment variables disabled so that no stored login (a credential helper,~/.netrc, anhttp.extraHeader) can answer for it, and given up on after 45 s even when the host never answers: a readable repository is created as public, an unreadable one through one of Coolify's private sources, asked for interactively or named with--source github-appor--source deploy-key. A GitHub App is picked with--github-app NAMEor from the list, and its access to the repository is checked through Coolify before anything is created, as is the branch when the app's listing of them is complete (Coolify relays GitHub's first page of 30; a branch beyond it is left for the first deployment).--source publicskips the probe. A deploy key is picked with--deploy-key NAMEor from the list, never implicitly, and the repository is stored in its SSH form for it.--create-deploy-key NAMEgenerates an Ed25519 key, registers it in Coolify, prints the public half once so it can be added to the repository, and stops; the private half is never printed or written. The plan and the result name the source; the JSON of a public plan is unchanged.- A documentation site under
docs/, built with Fumadocs (Next.js, MDX) and served as a Node.js server from a Docker image for deploying on Coolify at coolship.itrocas.com: an overview, a get-started path, concepts, a reference page per command derived from--help(includinginit,domain,login, andlogout), guides for CI, monorepos, and environment variables, and the platform limits. Every page is also served as Markdown (/docs/<page>.md, orAccept: text/markdown) withllms.txtandllms-full.txtfor assistants, and pages carry copy-as-Markdown and open-in-Claude/ChatGPT actions. The image is built by vinext (docs/Dockerfile), with anext buildimage (docs/Dockerfile.next) as the fallback; thedocs-siteworkflow builds both images on pushes tomainand pull requests that touchdocs/and smoke-tests their routes. - Refusals of creation and other mutations now carry the server's explanation (its
messageand field errors) in the error, so a repository Coolify cannot reach or a rejected value is reported as such rather than as a bare status code. Reads and server faults still report the status alone. - Shell completion:
coolship completion bash|zsh|fish|powershellprints a script for the shell, and the README says where each shell loads it from. An unknown shell name or an extra argument is invalid input (exit 2), like every other command's. previewtakes the target positionally, likedeploy:coolship preview api --pr 42.login: the global--contextnames the instance being saved when--nameis absent, and a line before the token prompt says where to create the token (Keys & Tokens; read, write, deploy; sensitive read for build logs and secret values).helpreaches nested commands:coolship help env pull,coolship help domain set.
Changed
env diff --exit-codenow exits 1 with no further message when there are differences, likegit diff --exit-code; the diff itself is still printed. It used to add anError: variables differline.linkandinituse the instance marked default in the Coolify CLI configuration without asking, exactly as every later command does;--contextoverrides it, and only a file with no default prompts for a choice (or, noninteractively, requires--context).- A rejected token (HTTP 401), a token missing an ability (403), and an
http://URL the server redirects (3xx) are explained on every command, not only bydoctorandlogin: the status is kept and a hint follows it once — runcoolship loginor checkCOOLSHIP_TOKEN; the token needs read, write, and deploy; use the https URL. - Transport failures name their cause in fixed words — the host name could not be resolved, the connection was refused, the TLS certificate could not be verified, the server did not answer with TLS or refused the handshake, the request timed out — instead of
request failed, and never repeat the transport's own text. - Missing credentials are reported as
No Coolify credentials at PATH; run coolship login, or set COOLSHIP_URL and COOLSHIP_TOKENby every command,doctorincluded (it used to sayrun coolify login); a file with no default instance says to pass--context NAMEor runcoolship login --default. logs --linesis checked against the server's range (1 to 10000) before any request, and the help says preview containers cannot be tailed.- A deployment that outlives
--timeoutsays so by name (--timeout 10m0s elapsed before the deployment finished; it continues on the server) instead ofcontext deadline exceeded; a single request that times out while the deployment is observed still saysrequest timed out, since the flag has not elapsed. A 429 fromPOST /deployis reported asserver deployment queue is full; a receipt saying a deployment of this commit is already queued is treated as a refusal even when it carries a UUID (Coolify never queues that one) and suggests--force, forpreviewtoo, since Coolify applies that check per pull request; and a deployment the server no longer holds while it is being observed (HTTP 404) is reported as dropped, with the same suggestion, instead of asmay still be running. unlinkin a monorepo lists every target the file holds in its confirmation, instead of an empty binding; the README says unlink removes every target.env pushshows the withheld keys it skips and the remote-only keys it keeps in the confirmation, before the push rather than only after it. The order of the listed changes is fixed (create, update, delete), andconfiglists overrides in a fixed order.devwith more than one argument and no--says to put the command after--, with the corrected invocation.link's own selection failures name the flags to use (link: no project named "x" in …; pass an exact name with --project, or a UUID with --project-uuid) instead of advising to runcoolship linkor to link a UUID, which only applies to later commands.loginvalidates the URL before asking for the token: a bare host is asked again interactively, and noninteractively the error names--urlor--name.
Fixed
- `e...
v0.2.0
Every command is now installable from a release, and the tool logs you in itself.
Added
loginandlogout: save a Coolify instance and API token after verifying them against the server, in the same file coolify-cli uses, so both tools share one login. The token is read without echo, or from stdin with--token-stdin; it is never a flag.- A
LICENSEfile (MIT). - Tagged releases: pushing a
vX.Y.Ztag publishes a GitHub Release with archives for Linux, macOS, and Windows on amd64 and arm64, achecksums.txt, and notes taken from this changelog. Tags with a pre-release suffix such asv0.2.0-rc.1are published as pre-releases, andlatestalways points at the newest full release. - A rolling
nightlypre-release built frommainon demand, replaced on every run and never markedlatest. - Continuous integration on every push and pull request:
gofmt,go vet, tests with and without the race detector, builds,shellcheckon the scripts, and a check that pull requests changing Go code also update this changelog unless labeledskip-changelog. scripts/install.sh: a curl-able installer for Linux and macOS that downloads a release archive from GitHub Releases, verifies its SHA-256 againstchecksums.txt, installs into$HOME/.local/bin(orCOOLSHIP_INSTALL_DIR/--dir), and prints a PATH hint for bash, zsh, or fish.COOLSHIP_VERSION/--versionselects a release, including pre-releases;--dry-runshows what would happen. It never runssudo.scripts/e2eand thee2eGitHub Actions workflow: every command is exercised against a live Coolify instance, confined to one example application, weekly and on each published release. The workflow is skipped when theCOOLSHIP_URLandCOOLSHIP_TOKENsecrets are absent.- Styled human output on terminals:
doctormarkers, deployment statuses,Warning:andError:prefixes, prompt questions,env diffmarkers, and key/value labels are colored with plain ANSI sequences. Styling is decided per stream, so a piped stdout stays plain while prompts on a terminal stderr are styled;NO_COLOR,TERM=dumb,CI, and the new--no-colorflag turn it off.--format jsonand streamed logs are never styled. - A one-page site in
site/, published to GitHub Pages frommainand buildable as an nginx image for deploying on Coolify.
Verify a download with sha256sum -c --ignore-missing checksums.txt. Full history: CHANGELOG.md.
v0.2.0-rc.1
Pre-release v0.2.0-rc.1. Changes since the last release, from the [Unreleased] section of CHANGELOG.md:
Added
loginandlogout: save a Coolify instance and API token after verifying them against the server, in the same file coolify-cli uses, so both tools share one login. The token is read without echo, or from stdin with--token-stdin; it is never a flag.- A
LICENSEfile (MIT). - Tagged releases: pushing a
vX.Y.Ztag publishes a GitHub Release with archives for Linux, macOS, and Windows on amd64 and arm64, achecksums.txt, and notes taken from this changelog. Tags with a pre-release suffix such asv0.2.0-rc.1are published as pre-releases, andlatestalways points at the newest full release. - A rolling
nightlypre-release built frommainon demand, replaced on every run and never markedlatest. - Continuous integration on every push and pull request:
gofmt,go vet, tests with and without the race detector, builds,shellcheckon the scripts, and a check that pull requests changing Go code also update this changelog unless labeledskip-changelog. scripts/install.sh: a curl-able installer for Linux and macOS that downloads a release archive from GitHub Releases, verifies its SHA-256 againstchecksums.txt, installs into$HOME/.local/bin(orCOOLSHIP_INSTALL_DIR/--dir), and prints a PATH hint for bash, zsh, or fish.COOLSHIP_VERSION/--versionselects a release, including pre-releases;--dry-runshows what would happen. It never runssudo.scripts/e2eand thee2eGitHub Actions workflow: every command is exercised against a live Coolify instance, confined to one example application, weekly and on each published release. The workflow is skipped when theCOOLSHIP_URLandCOOLSHIP_TOKENsecrets are absent.- Styled human output on terminals:
doctormarkers, deployment statuses,Warning:andError:prefixes, prompt questions,env diffmarkers, and key/value labels are colored with plain ANSI sequences. Styling is decided per stream, so a piped stdout stays plain while prompts on a terminal stderr are styled;NO_COLOR,TERM=dumb,CI, and the new--no-colorflag turn it off.--format jsonand streamed logs are never styled. - A one-page site in
site/, published to GitHub Pages frommainand buildable as an nginx image for deploying on Coolify.
Verify a download with sha256sum -c --ignore-missing checksums.txt. Full history: CHANGELOG.md.