Skip to content

Releases: cubeshipd/cubeship

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 11 Sep 02:34

Fixed

An app that pulls from this instance's own registry could not be
deployed.
It failed with:

lookup cubeship-registry on 127.0.0.53:53: server misbehaving

which reads as a broken registry and is nothing of the kind. The
reference the pull was given began with the registry's container name,
and a container name is only resolvable by other containers on the same
Docker network. The pull is performed by Docker itself, which is not on
that network — so the name was never going to resolve, and the registry
it names was up and answering the whole time.

Pulls now use the registry's address on the host, which is published
either way and works whatever else is running. Nothing to change: push
and deploy as before.

It only ever affected apps whose image is pulled. An app Cubeship
builds — from a Dockerfile or with Railpack — has its image handed
straight to Docker with no pull at all, so an instance that only builds
never met this.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 01:46

Changed

Where a Docker image comes from is three choices, not two cards. It
was "Cubeship's registry" or "Another registry", above a box you typed a
reference into. The second was never one thing: it is however many
registries you have connected, and picking it told you nothing about
which.

Now you pick the registry, then the image, then the tag — and each is a
list this instance fetches:

  • The registry is Cubeship's own, every registry you have connected,
    and Docker Hub. Docker Hub is there whether or not you have connected
    it, because a public image needs no login.
  • The image comes from that registry's own catalogue. Docker Hub has
    no public catalogue, so you type the image there and the tag is still
    listed; a registry that will not list what it holds behaves the same
    way.
  • The tag is newest first, and the newest is already chosen.

On Cubeship's own registry there is no image to choose: it is this app's
own path, shown and locked. A push is matched to an app by its
reference, so pointing one app at another's path would deploy the wrong
app every time somebody pushed.

Added

An app can be pinned to a tag, and that is how deploy-on-push is
turned off.

Until now an app on this instance's registry deployed on every push and
there was no way to say otherwise. Choose a tag and it runs that one,
deployed when you ask for it — a push is ignored from then on, including
a push of the very tag it is pinned to. An app pinned to v1.0 is one
somebody decided should run v1.0, and moving it because a notification
arrived would undo that decision with nobody watching.

The switch and the tag are the same setting seen twice, which is why the
screen hides one when you use the other: an app cannot both follow every
push and be pinned, so there is no way to set them to disagree.

Nothing changes for an app you already have. Every app starts
unpinned, which is exactly what it does today: a push to this instance's
registry deploys it, and an app pulling from anywhere else runs
latest.

For the API and the CLI, tag joins image on POST /apps and PATCH /apps/{ref}, autodeploy is reported beside it, and cubeship app create gained --tag. cubeship app deploy --tag is unchanged and
still wins over whatever is pinned — what you asked for beats what was
configured, and the deployment history records the tag that actually
ran.

v0.4.3

Choose a tag to compare

@github-actions github-actions released this 11 Sep 01:04

Fixed

"What Traefik says" was unreadable. A name waiting on a certificate
showed something like r[90m2026-09-10T23:57:50Z[0m [31mERR[0m where
the reason was meant to be, and that field is the only place an ACME
refusal is written down at all.

Two things overlapped. Traefik colours its own log whether or not
anything is reading it as a terminal, and the escape byte was being
dropped on its own — which leaves the rest of each colour code sitting
in the text. And the binary header Docker puts in front of each chunk of
a log was being removed by discarding its unprintable bytes, which the
length byte is not for any line of ordinary length.

Both come off properly now, so the field reads as what it is:
2026-09-10T23:57:50Z ERR Unable to obtain ACME certificate for domains…, timestamp included.

v0.4.2

Choose a tag to compare

@github-actions github-actions released this 11 Sep 00:54

Fixed

A firewall rule for an exposed database or object store never admitted
anything.
If you published a Postgres on 15000 and allowed 15000, the
rule was written, listed, and matched no packet ever: rules about
forwarded traffic are consulted after Docker has rewritten the
destination, so what arrives is addressed to 5432 and a rule naming
15000 cannot see it.

It survived this long because the only ports that get rewritten are the
ones you open yourself. Everything Cubeship publishes for itself —
Traefik's 80 and 443, the dashboard's 3000 — is the same number inside
and out, so the feature worked for every port the product opens and for
none of yours.

Rules are now written for the port the container is actually on, and the
Firewall screen shows the translation: 15000/tcp → 5432.

If you have already put your published ports behind the firewall,
the rules you wrote for a database are the old, inert kind. After
updating, that port shows as admitted by nothing — add the rule again,
and delete the old one, which is doing nothing and only makes the list
harder to read.

A certificate that failed to issue was never asked for again.
Traefik asks a certificate authority only when its configuration
changes, so a name whose first attempt failed — a DNS record written a
minute too late, an hour when Let's Encrypt could not reach your
nameservers — stayed without one until something unrelated happened to
change the routes. On a settled instance that could be never, and the
only way out was redeploying an app with nothing wrong with it.

This instance now asks again every half hour, for as long as any name is
waiting, so a name that starts resolving here gets its certificate
without you doing anything. Only names Traefik is actually waiting on:
an instance with no domain, or an app not redeployed since the name was
added, are still things for you to do, and asking would not move either.

Added

Postgres 18 is on the list of versions a new database can run.

It needed more than the number. Postgres 18's image moved where it keeps
its data and declared its volume somewhere else, so a database created
on it would have come up, worked, and kept everything in a place this
instance does not know about — in no backup of the data directory, and
thrown away the next time the container was replaced. Cubeship now says
where the data goes rather than letting the image decide.

Existing databases are untouched: a datastore's version is fixed for its
life, which is what keeps a data directory readable by the server that
wrote it.

Changed

Creating an account hands back a password, not an API key. The
account it used to make could not sign in anywhere: a key is what the
CLI and MCP clients carry, the dashboard wants a password, and nothing
here lets a new person set a first one — there is no invite mail and no
reset flow. So you created somebody an account, handed them a
credential, and it opened nothing you had given them the address of.

The password is shown once — this instance keeps only its hash — and
whoever it belongs to changes it from their own account screen. API keys
stay self-service, made by the person who wants one.

This changes the response, so anything scripted against it needs a
look: POST /users returns password where it returned api_key, and
takes an optional password of your own. cubeship user create prints
the password and gained --password.

The revoke_api_key tool told agents the last key cannot be
revoked.
It can, deliberately — a leaked key has to be able to go
now — and the description says so, along with what revoking it costs.

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 10 Sep 23:37

Changed

Users is in the sidebar under Platform, beside Credentials, rather
than tucked into your own settings. Who can reach this instance is the
same kind of fact as which registry it pulls from — your password and
the colours you see are the other thing, and those stay under your name.

A member who opens it is told it is an admin's to see, rather than shown
an empty table.

Fixed

The screen is put together like the rest of the dashboard. It had a
table of its own and a bare dropdown: the table did not sort or show
that it was loading, and the dropdown was a different height from the
field beside it. Both are the shared components now.

Adding somebody is above the table, because that is what brings
anybody to the screen. The table answers "who is there", and you already
know when it is only you.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 22:56

Who has access

Account → Users, for an admin. Add someone, see who holds a way in,
revoke every credential one account has, or remove it entirely.

The API for all of this has been there since the first release with
nothing in the dashboard reaching it — this is the screen.

Adding somebody hands back an API key, shown once, and no password.
The key is what gets them in; the password is theirs to set, and this
instance only ever keeps hashes of either.

Two things it refuses, and it says so before you click rather than
after: the account you are signed in as, and the last admin. Nothing in
the API can make an admin without one.

It is an admin's screen including the reading. What the list says is who
can get in, which is not something a member needs and is exactly what
somebody probing would want.

Seven palettes

Account → Appearance. Cubeship, Mono, Hacker, Red, Orange, Pink,
Purple.

Every one of them is dark, and that is a decision: this is a console for
a machine, read beside a terminal, and a light one would be the only
screen on that desk that is. Each changes colour and nothing else
the layout, the type and the square corners are the product.

Your choice is saved to your account rather than to the browser, so it
follows you to another machine instead of leaving one person with two
different-looking instances.

Your settings are yours

/account is tabs now — how you sign in, what it looks like to you, and
who else can get in — and it is reached from the menu under your name
rather than from the sidebar. That sidebar section held one item, and
what is yours is not what the instance is made of.

Settings in the sidebar is still the instance's: its domain, its
contact address, when it updates itself. Nothing moved there.

v0.3.3

Choose a tag to compare

@github-actions github-actions released this 10 Sep 22:08

Fixed

Scheduling an automatic update refused every timezone. Not an
unusual one: America/Bahia, Europe/Lisbon, any of them. The daemon
runs on an Alpine image, Alpine ships no timezone database, and nothing
in the daemon carried its own — so it could not recognise a single name
and said so about each one in turn.

The database is in the binary now, so it no longer matters what the
image underneath has. Set the hour and the timezone in Settings →
Automatic updates
and it will hold.

It is also no longer quiet about falling back to UTC. That combination —
a name it could not load and a silent fallback — would have been an
instance updating itself at the wrong hour for as long as nobody
happened to be watching at the time.

v0.3.2

Choose a tag to compare

@github-actions github-actions released this 10 Sep 21:38

Fixed

An instance could update once. The daemon is replaced by a throwaway
container, and that container was asked to delete itself with a flag
that never reached Docker — so the first update left it behind, and the
second failed on the name being taken.

It failed between the two halves: the dashboard had already been
replaced and the daemon had not. So the instance looked updated, kept
saying it was on the release before, and went on offering the one it was
already showing you.

If you are seeing that, the update below fixes it for good — but this
instance still has the old updater in the way, so clear it first:

docker rm cubeship-daemon-updater

Then press Update. From this release on, the name is cleared before each
update whatever happened last time: a machine rebooted mid-update should
not be a machine that can never update again.

The dashboard could come back a version behind. The daemon's
environment is carried over when it is replaced, and one variable in it
names the dashboard's image — so a daemon that came back still pointing
at the old one would have put the old dashboard back on its next
restart. An update that looks done and undoes itself on a reboot.

Changed

The link to the repository is in one place now, beside your name in the
sidebar, and carries the project's star count.

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 10 Sep 20:33

Fixed

The button in this dialog's footer was sitting outside the panel it
belongs to — the footer carries margins that assume the popup keeps its
padding, and this dialog takes it off to hold the header and footer
still while the notes scroll.

Added

Release notes are in the user menu. They used to appear once after
an upgrade and then be gone. Opened from the menu they are the history —
every release up to the one this instance runs — and closing marks
nothing, because reading them again is not an event.

The menu also carries the account and a link to the repository, and the
sidebar's foot has that link as an icon: the one thing in this dashboard
that is not part of your instance.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 19:38

The notes you are reading

"Got it" marks them read. It did not: the button closed the dialog
and told the daemon nothing, so the next reload showed the same notes
again and only the × ever ended them. It is one path now, and the
instance is told before the dialog goes — a reload a moment later was
cancelling the request that said so.

No more sideways scroll. A release note is full of commands, and one
long enough to overflow was moving the whole dialog rather than itself:
the prose went with it, and the button slid out from under the pointer.
A command block now scrolls on its own.

The header and the button stay put while the notes scroll between
them. On a long release the button was below the fold, which is a poor
place for the only thing that dismisses something.

The dialog that offers an update got all three, because it is the same
dialog showing the same kind of text.

Updating to this one

This is the first release you can install from the dashboard — 0.2.0 is
what put the button there. If this instance is on 0.2.0, the dialog
offering 0.3.0 is the whole of it.