# Update a server from its git repository

> Deploy a new commit to a Node.js or Python server that clones a git repository, rebuild or redeploy it, and roll back to an earlier release.

Source: https://www.coritan.com/docs/managed-containers/server-releases/

In the dashboard:

- /dashboard/deployments/…/releases: https://www.coritan.com/dashboard/deployments

A server that runs your own code from a git repository, such as a Node.js or Python server with a Git address, is updated by a *release*. A release puts one commit of the repository on the server. We fetch the commit and check it before the server is touched, and we keep the version it ran before until the new one has started and stayed up. If the new one does not start, the previous version is put back and started again.

Use a release whenever you have pushed new code. You do not need **Reinstall server…** to update: a reinstall deletes every file on the server, and a release keeps the files that are not in your repository.

A web service built from git has releases of another kind, built for it and started beside the live one ([Deploy, promote and roll back releases](/docs/managed-containers/releases/)). A game server or a database has none.

## Before you begin

- The server runs software that clones a git repository: one of the Node.js or Python runtimes.
- Its `GIT_ADDRESS` startup variable names a repository on GitHub or GitLab, as a web address (`https://github.com/example/api`) or as `example/api` for GitHub. `GIT_BRANCH` names the branch, and `main` is used when it is empty. Both are on the server's **Settings** tab, on the **Startup variables** card ([Server settings](/docs/managed-containers/settings/#startup-variables)).
- For a private repository, a repository token that can read it ([Set the repository token](#repository-token)).
- You own the server, or you are a subuser with the `software.update` permission. `software.read` is enough to read the releases ([Share a server with other users](/docs/managed-containers/users/)).

A server with a Git address on another host has no **Releases** tab. It clones the repository when it installs, and the tab's address says so.

## What a release changes {#what-changes}

A release replaces the files your repository holds, and removes a file the last release held that this commit no longer has. Everything else on the server stays where it is: a `.env` file, installed dependencies such as `node_modules`, uploads, a database file.

Two things to know:

- A change you made on the server to a file the repository holds is replaced by the repository's version. Commit a change you want to keep.
- A file of your own that a release replaces is not deleted. This happens when the repository holds a file with the same name as one you made on the server, and on the first release of a server that already had files. The release moves each such file into `.coritan/kept/`, in a folder named after the release, and its log names the folder.

The server stops while its files are replaced, and starts again if it was running. A server that was stopped stays stopped.

## Deploy a new commit {#deploy}

1. In the dashboard, go to **App Deployment**, open the server, then the **Releases** tab.
2. Select **Deploy…**. A page opens, titled **Deploy a release**.
3. Under **Branch, tag or commit**, leave the server's branch to deploy its newest commit, or type another branch, a tag or a commit.
4. Select **Deploy**.

A message says `Deploy started.` and the release's page opens. It shows what the release is doing: `Fetching the commit`, `Copying the files to the server`, `Replacing the files`, `Starting the server`, then `Watching the server after the start`. While it reads `Fetching the commit`, nothing on the server has changed, and **Cancel release…** stops the release. Once it copies files it finishes on its own, or puts the previous version back.

When the server already runs the newest commit of the branch, the release ends as **Cancelled** with `Nothing to deploy`, and the server is not restarted. Use **Rebuild…** or **Redeploy…** to put its files back.

## Rebuild or redeploy {#run-again}

Both are under **Run again** on the **Releases** tab, once the server has a live release.

| Action | Use it when | What it does |
| --- | --- | --- |
| **Rebuild…** | The dependencies are broken, or you want a clean install of them. | Fetches the newest commit of the branch again and clears the software's dependency folder (`node_modules` for Node.js, `.local/lib` for Python), so the dependencies install fresh when the server starts. |
| **Redeploy…** | A file from the repository was changed or deleted on the server and you want it back. | Puts the files of the live release back as they were committed, and restarts the server. Nothing is fetched. |

Each asks first, and you confirm with **Rebuild** or **Redeploy**. The **Repository** card names the folders a rebuild clears, beside **Cleared by a rebuild**.

## Roll back {#roll-back}

1. On the **Releases** tab, find the release you want to return to. One you can return to reads **Replaced**.
2. Open its menu and select **Roll back to this…**, or open the release and select the same button.
3. Select **Roll back**.

A rollback is a new release that runs the stored files of the earlier one. Nothing is fetched, so it works while the git host is down or your token has expired. If the server does not stay up on those files either, the version it ran before the rollback is put back.

We keep the files of a server's newest releases and of its live release. A release whose files are no longer kept has no **Roll back to this…**. Deploy its commit instead: type the commit under **Branch, tag or commit**.

## Read a release {#read}

The **Releases** tab lists every release, newest first, with its commit, what started it and how long it took. Select one to open it.

| Status | Meaning |
| --- | --- |
| **Queued** | It waits to start. |
| **Fetching** | We are downloading and checking the commit. The server has not changed. |
| **Deploying** | The files are being copied and replaced, or the server is starting. |
| **Live** | It is the release the server runs. |
| **Failed** | It did not finish. `Previous version put back` under it means it had begun to replace files, and they were moved back. |
| **Cancelled** | It was cancelled, or there was nothing new to deploy. |
| **Replaced** | A newer release took its place. |

A release's page has its **Log**, each step it took with the time, and its **Details**: the repository, the commit, who started it and whether its files are kept for a rollback. A release that failed to start shows the server's last lines of output in the log, which is where a missing module or a syntax error shows.

## Set the repository token {#repository-token}

A private repository needs a token that can read it. We store it sealed and use it only to fetch the repository. It is never shown again, sent to the server or written to a log.

1. On the **Releases** tab, find the **Repository** card and select **Set token…**, or **Replace token…** when one is set.
2. Paste the token under **Repository token**. On GitHub, use a fine-grained personal access token with read access to Contents for this repository.
3. Select **Save token**.

The token applies from the next release. To remove it, open the same page and select **Remove token…**, then **Remove token**. Changing the token needs the `startup.update` permission.

> [!WARNING]
> Do not put a token inside `GIT_ADDRESS`, as in `https://token@github.com/example/api`. A startup variable shows to everyone who can read the server's settings. A release still works with such an address, and the **Releases** tab warns `The Git address has a token in it`. Set the token on its own page, then remove it from the address.

## Reinstall a server that clones a repository {#reinstall}

**Reinstall server…** on the **Settings** tab still deletes every file on the server. For a server with releases it does two things first and one after:

1. It fetches the repository. When the repository does not answer, or refuses the token, the reinstall does not start and no file is deleted. The message says why.
2. It deletes every file and installs the software again.
3. It puts the commit it fetched back on the server, as a release that reads `Install` under **Started by**.

Files the repository does not hold are gone after a reinstall. Take a [snapshot](/docs/managed-containers/snapshots/) first to keep them, or deploy a release instead.

## Result

The **Releases** card reads, for example, `4 releases, newest first. Live: 3b9d7f1.` and the release you started reads **Live**. Your server runs the new commit, and its other files are as they were.

## With the API

Every path starts with `https://api.coritan.com/api/v1/client/servers/{uuid}`, with the server's ID. Send your access token as `Authorization: Bearer $CORITAN_TOKEN`.

Deploy the newest commit of the server's branch, or name a branch, tag or commit in `git_ref`:

```bash
curl -X POST https://api.coritan.com/api/v1/client/servers/$SERVER/releases \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"git_ref": "v1.4.0"}'
```

The answer is `201` with the queued `release`. Read it again with `GET /releases/{release_uuid}` until its `status` is `ready` or `failed`:

```json
{
  "release": {
    "uuid": "8a4c2e6f-1d7b-4c3a-9e5f-2b8d4a6c1e37",
    "status": "ready",
    "stage": "done",
    "trigger": "manual",
    "source": "git",
    "git_ref": "v1.4.0",
    "git_sha": "3b9d7f1a5c2e8b4d6f0a9c3e7b1d5f2a8c4e6b10",
    "is_current": true,
    "can_rollback": false,
    "restored": false,
    "error": null,
    "log": "[11:05:22] Downloading example/api at v1.4.0 from GitHub.\n[11:06:14] The release is live.\n"
  }
}
```

| Call | What it does |
| --- | --- |
| `GET /releases` | The releases, newest first (`limit` 1 to 100, `offset`), with `git` (what the server clones), `can` (whether a release may start now, and why not), `current_release` and `in_flight`. |
| `POST /releases` | Deploy. An empty body deploys the newest commit of the branch. |
| `POST /redeploy` | `{"rebuild": true}` is Rebuild, `{"rebuild": false}` is Redeploy. |
| `POST /releases/{release_uuid}/rollback` | Roll back to that release. |
| `POST /releases/{release_uuid}/cancel` | Cancel a release that is `queued` or `building`. |
| `GET /git-token` | `{"git_token_set": true}` when a token is set. The token is never answered. |
| `PUT /git-token` | `{"token": "..."}` sets or replaces the token. An empty `token` removes it. |

`status` is `queued`, `building`, `deploying`, `ready`, `failed`, `canceled` or `superseded`. The dashboard shows `building` as **Fetching**, `superseded` as **Replaced**, and `ready` as **Live** while `is_current` is true. A failed release has `error`, `error_code` and `restored`, which is true when it had changed files and the previous version was put back.

A refusal answers `{"detail": {"error": "...", "message": "..."}}`:

| Status | `error` | Meaning |
| --- | --- | --- |
| `409` | `no_git_source` | The server clones nothing we can fetch. `message` says what is missing. |
| `409` | `release_in_progress` | A release is already on its way. Wait for it, or cancel it. |
| `409` | `server_busy` | The server is installing, moving, being restored or suspended. |
| `409` | `install_in_progress`, `snapshot_in_progress` | A software install, a snapshot or a restore is running on the server. |
| `409` | `already_current` | A rollback named the live release. |
| `409` | `not_rollbackable`, `archive_not_stored` | The release was never live, or its files are no longer kept. |
| `409` | `not_cancelable` | The release is replacing files, or has ended. |
| `422` | `invalid_ref` | `git_ref` is not a branch, tag or commit name. |
| `429` | | More than 12 release actions on one server from one account in 10 minutes. |

The server's own read, `GET /api/v1/client/servers/{uuid}`, has the same `git` block. It is `null` for a server that clones nothing, and `git.releases` is `false` for a repository on another host.

## Troubleshooting

`GitHub refused the repository token for example/api (HTTP 401). It may have expired or been revoked.`
: The token no longer works. [Replace the repository token](#repository-token), then deploy again. The server kept running its current version.

`GitHub found nothing for example/api at main (HTTP 404).`
: The branch, tag or commit does not exist, or the repository is private and no token is set, or the token cannot read it. GitHub answers 404 for a private repository it will not show. Check `GIT_BRANCH`, then the token.

`The server stopped 4 seconds after it started. The previous version is back and running.`
: The new commit does not start. Open the release and read the end of its **Log** for the server's last output. Fix the code, push, and deploy again.

`The server's disk has 12 MB free and the release needs about 40 MB while it is put in place.`
: A release needs room for the new files beside the old ones until it is live. Free some space on the **Files** tab, then deploy again.

`The reinstall was not started and no file was deleted.`
: A reinstall fetches the repository before it deletes anything, and the fetch failed. The rest of the message says why. Fix it, then reinstall again.

The server was installed, and its directory is empty
: The repository could not be fetched when the server was installed. The **Releases** tab shows the failed release with the reason. Fix it, then select **Deploy…**. There is no need to reinstall.

**Deploy…** is missing, and the page says `A release cannot start right now`
: The message under it names what the server is busy with. Wait for it to finish.

## Next steps

- [Server settings](/docs/managed-containers/settings/) to change `GIT_ADDRESS`, `GIT_BRANCH` and your app's own variables.
- [Take a snapshot](/docs/managed-containers/snapshots/) to keep a copy of the whole server, including the files a release does not touch.
- [Use the console and power controls](/docs/managed-containers/console/) to read your app's output while it runs.

## API

- `GET /api/v1/client/servers/{uuid}/releases`: List the server's releases (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers-releases/#op-get-api-v1-client-servers-uuid-releases)
- `POST /api/v1/client/servers/{uuid}/releases`: Deploy a commit to the server (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers-releases/#op-post-api-v1-client-servers-uuid-releases)
- `GET /api/v1/client/servers/{uuid}/releases/{release_uuid}`: Read one release with its log (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers-releases/#op-get-api-v1-client-servers-uuid-releases-release-uuid)
- `POST /api/v1/client/servers/{uuid}/releases/{release_uuid}/rollback`: Roll the server back to an earlier release (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers-releases/#op-post-api-v1-client-servers-uuid-releases-release-uuid-rollback)
- `POST /api/v1/client/servers/{uuid}/releases/{release_uuid}/cancel`: Cancel a release before it changes the server (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers-releases/#op-post-api-v1-client-servers-uuid-releases-release-uuid-cancel)
- `POST /api/v1/client/servers/{uuid}/redeploy`: Rebuild or redeploy the server's release (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers/#op-post-api-v1-client-servers-uuid-redeploy)
- `GET /api/v1/client/servers/{uuid}/git-token`: Read whether a repository token is set (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers/#op-get-api-v1-client-servers-uuid-git-token)
- `PUT /api/v1/client/servers/{uuid}/git-token`: Set, replace or remove the repository token (https://www.coritan.com/docs/api/reference/client/deployment-servers/servers/#op-put-api-v1-client-servers-uuid-git-token)
