Step 6. Deploy

This page shows you how to deploy. Deploy means: Deployment Manager sends the bundle to every server of the installation over SSH, and runs the installer there. The page follows the deploy from when you press Deploy until every service is up.

Before you start

This section lists what must be ready before you deploy.

The server may have no SSH route from Deployment Manager. Then use ↓ Download instead and carry the bundle to the site. See Step 4. Create the bundle.

For the product words on this page, see the glossary.

Steps

This section shows how to run one deploy.

  1. Open the Installations tab.

  2. Find the card of your installation. Press Deploy.

  3. The dialog Deploy (installation name) — where it goes opens. It shows what the panel found on each server. Read it. See Before anything is copied.

  4. Press Continue →.

  5. The panel may ask you to apply database migration steps by hand first. It does this only for a database you run yourself. Run the SQL from the bundle. Type applied. Press Continue →.

  6. The secrets step opens, if the deploy needs secrets. Type each secret. Or press Generate where it is offered.

  7. Press Copy all values. Keep the values somewhere safe. After the deploy you cannot see them here again.

  8. Press Deploy →. The deploy starts. The panel shows its progress live. See What you see while it runs.

  9. Wait. Each server that finishes shows deployed. When the whole deploy succeeds, the screen Installed — and the installation name opens.

  10. Go to Step 7. Check that it works.

Maybe the screen filled in a value that you did not copy. Then, if you deploy or close the window, the panel asks you to save the passwords first.

Before anything is copied

This section lists what the panel checks before the deploy starts. The panel first reads every bound server. It reports two things about each one. Nothing is copied or run before that.

What the install directory currently holds:

State What it means

Empty

Nothing is installed here. This will be a fresh install.

Ours

This installation is already here. It will be updated in place.

Occupied by another of our servers

The directory holds a different server from this tool. Two servers cannot share a directory. The panel offers its own directory instead, and tells you. Commands that expect the standard directory will not find the install there.

Occupied by an installation this tool did not create

The same offer and the same warning. Here the directory is one this tool has no record of.

Could not be read

The panel could not reach the server. The deploy cannot start until every server answers. Check SSH access, then open Deploy again. If the server answers but one directory cannot be read, the panel offers its own directory instead.

What the server is missing:

State What it means

Docker not installed

The deploy cannot run here until Docker is installed.

Compose v2 plugin missing

Docker is present, but docker compose is not. Install the plugin and try again.

The offered directory may not be the standard one. Then the panel shows a warning. Every guide, runbook and support script assumes /usr/local/ft-system. If a deploy used another directory, give that path wherever you use those documents.

Every deploy also runs readiness checks on each server. They run after the bundle is copied, but before any container is started or stopped. See the readiness checks.

What you see while it runs

This section explains what the panel shows during the deploy. The panel shows a step list and a transcript together, live. After docker compose up -d returns, there is one row per service. Each row shows one word for what the service is doing:

Word Meaning

starting

Normal. After about three minutes the row also shows how long it has been starting. This is still not a failure. It means this service is slower than usual.

ready

Reporting healthy.

stopped

The container exited.

unhealthy

Running, but its health probe is failing.

restarting

Docker is restarting it repeatedly.

When every step for a server finishes, the server’s result is one of deployed, failed, or not attempted. Not attempted means the run stopped on an earlier server before it reached this one. Sometimes the panel cannot check the server itself. Then the reason is one of two: the server could not be reached, or the server refused the stored credentials.

Where the service rows come from. Docker Compose itself reports them. A container renamed by hand can stop the short progress note under the current step from updating. Check the container directly on the server before you decide.

How long a deploy takes

This section gives the normal time of a deploy. The wait for services to report healthy has a limit of ten minutes per server. A three-server install that pulls images and then waits can take half an hour. This is normal.

Deploy waves: why a second server waits

This section explains deploy waves. Two servers can depend on each other in a cycle. For example, host 1 needs something on host 2, and host 2 needs something on host 1. Then the deploy is split into waves. A wave is a set of server visits that must happen in a fixed order. The tool calculates the waves from the service dependencies. Nobody enters them by hand. See Step 1. Choose a topology for how a topology produces waves.

Before the run starts, the panel states the plan in one line. It names every wave and the servers in it. For example: "deploying 3 server(s) in 2 waves, each waiting for the one before — wave 0: db-host; wave 1: host1, host2". A plan with one wave has no such line.

During the run, the transcript marks the start of each wave for its server. A host may be visited more than once. Then the step list and the roll-call label each visit with its wave (host1 · wave 1). A host with no cycle in its plan has one visit. A host that runs only services with no wave dependency is deployed last. It is labelled no wave. It is not added to wave 0.

A server’s second visit does not copy the bundle again. It does not change the environment file again. The transcript says so. So its step list is shorter than on the first visit. This is normal. It does not mean the install was skipped.

In one wave, the servers install at the same time. The next wave starts only when every server in this wave has finished. If a server fails, the other servers in the same wave still finish. Then the run stops. Every server in the later waves is not touched. It is reported as not attempted. A server that failed has something on it to check. A server that nobody reached has nothing.

Deploying over an existing install

This section explains a deploy into a directory that already holds this installation. For the full update steps, see Update an existing installation.

The panel reports such a directory as ours. This is the normal case. A new deploy is how these changes reach a server that already has the installation:

  • a topology change

  • a variable edit

  • a new release version

Two things happen that a first install never has.

The .env files are merged, never replaced. The host .env and every <service>/.env already in the directory stay in place. Nothing is asked. For each variable this deploy would write:

On the server What happens

The variable is missing

It is added at the end of the file. A line above it says the installer added it.

The value was edited on the server

The server’s value stays. The deploy log names the variable, never its value.

The value is what Deployment Manager wrote last time, and it has since been changed in Deployment Manager

The new value replaces it. The installer knows this from a copy of what it delivered last, .env.delivered, kept next to each file.

A secret (a password, key or token)

The server’s value always stays. The database was created with the old password. A different password would not work with it. Every service would then fail to sign in.

A variable this version no longer reads

It stays, and the log names it. You must move a value under an old name by hand.

A secret on the server is never overwritten. So the Deploy form does not ask for a secret that every server needing it already has. It names those secrets instead. A server may turn out not to have it. Then the installer stops at its own secrets check, before anything starts.

When a file changes, the old version is kept next to it as .env.previous. The first deploy after this behaviour was released has no .env.delivered to compare with. So it only adds variables and changes none.

The warning deploy.sh prints before starting services. A database creates its accounts and schemas only on the first start with an empty data directory. On a new deploy, the data directory already exists. Those accounts do not change, whatever password is now in .env. deploy.sh prints a WARN block about this for each database that already holds data, right before it starts the containers. A deploy may later fail with the database refused the credentials. The cause is almost always this: the database still expects the old password, not the one you just typed. See Whose failure is it?.

Configuration files that were edited on the server. The .env merge above applies to the .env files. All other configuration files in the bundle get a simpler version of the same protection. Examples: application settings, certificates, database init scripts. It works the same way for all three ways a bundle reaches a server:

  • the panel’s Deploy

  • deploy.sh --mode bootstrap run by hand

  • an offline install from a carried archive

Before anything is copied, each file on the server is compared with what the last deploy wrote there:

What is found What happens

The file still matches what was last written

Nobody changed it. It is replaced with the new one. The old file is kept next to it as <file>.previous.

The file differs

Somebody edited it on the server. It is left as it is, and the deploy log names it. The deploy continues and installs everything else.

This is the first deploy after this protection shipped

Nothing is recorded yet to compare with. So every existing file is kept. The log says this once, as a count. The comparison works from this installation’s next deploy onward.

The server’s own state could not be read

Nothing is changed, and the log says so. This is the safer choice when the comparison itself cannot be trusted.

A service’s own contract can override the comparison for a specific file with config_policy:. It has two values:

  • keep: never write over the file, whatever the comparison says. Use this for a certificate or key a customer supplies.

  • replace: always write our file, even if the server’s file is different.

There is no merge. If a file needs more than keep or replace, raise it as a case. It is not a setting you can choose.

This does not cover the everyday case of changing a value and deploying again. Configuration files never contain values you typed. Those values are in .env, which the section above covers.

An update to a newer product version can include schema migrations that run in order. You decide this earlier, in the installation wizard’s own step for it. See Step 3. Fill in the variables and Update an existing installation.

If something goes wrong

This section explains what to do when a deploy fails. The full failure-code reference is in When something goes wrong. It lists every message the panel can show and what to do about each one.

Retrying a failed server

This section explains the retry. After a failure, press Run it again. Then confirm with Run it again on (the server name), the "run it again on this server" action. It runs the installer again in the directory that already holds the install. It does not scp the bundle. It does not ask for secrets again. The .env is already on that server from the deploy that just ran.

The retry is offered only when the run already copied something to the server. A failure before that point left nothing on the server to retry. In that case the panel offers a full Deploy.

The retry refuses to run on a half-copied install. That is a directory with a compose file but without its .env or deploy.sh. It means a copy stopped partway. A retry there would make the problem worse. The panel says so and tells you to run a fresh Deploy.

A retry runs the whole server again, every wave of it, in one go. It also runs all the readiness checks again.

What a retry cannot fix. It has no new bundle and no new secrets. It runs the script already on that server with the configuration already there. A wrong value or a bad bundle needs a fresh Deploy, not a retry.

The result of a retry is added to the roll-call. It does not replace it. For example, a three-server run failed on the second server. The second server is retried and succeeds. The third server still shows not attempted.

Rolling back the .env

This section explains the rollback. A server’s install directory may hold an earlier .env. A previous deploy or retry kept it. Then the panel offers Put the previous .env back.

  1. Press Put the previous .env back.

  2. Confirm. Then the panel does this:

    1. The current .env and the earlier one swap places.

    2. The installer runs again.

    3. Every container on that server starts again with the older configuration.

Nothing is deleted. The replaced file becomes the new .env.previous. Press the button again to restore the current one.

The panel shows two different results:

What the panel says What happened

There was no earlier .env on this server to go back to

A refusal. Nothing was changed, so there is nothing to undo. If the current .env is wrong, edit it directly on the server or deploy again.

The earlier .env could not be put back — check it on the server

The swap had already reached the server when something went wrong. Check .env and .env.previous in the install directory by hand before you deploy again.

A rollback does not find why an install is broken. The panel never says that it does. It is offered everywhere a retry is offered. The app cannot know if an earlier .env exists without opening SSH to check. It does not open SSH until you press the button.

Whose failure is it?

This section helps you see who must fix a failure. The panel’s messages are written around this question. Two of the most common are here:

  • The database refused the credentials. The database starts correctly and then refuses every service. This is the .env problem from Deploying over an existing install. A database creates accounts and schemas only on its first start with an empty data directory. So a new deploy over an existing data directory keeps the old credentials, whatever you just typed. Use the passwords that database already has.

  • A container did not come up — check its logs. First read the broken container’s own health probe. Then read its log on that server. Do not trust compose’s own dependency line (dependency failed to start: container X is unhealthy). That line always names the container that was waiting. It never names the one that actually broke.

Good to know

This section lists facts for special cases.

Running the installer by hand

This section lists two facts for when you run deploy.sh yourself, not from the panel.

  • It needs bash 4 or newer. An older one, such as the version macOS ships, stops at once with the message bash >= 4 required. Install a newer bash and run it again.

  • It marks a refusal and a crash with different FAIL lines. A refusal names its reason. A crash names the step it stopped in (in_stage).

What a restart of Deployment Manager does to a running install

This section explains what happens to running deploys when the tool restarts. Examples of a restart: a release, a moved pod, an evicted node. Without protection, a restart would stop an install in the middle of a copy or of up -d. It would stop on a customer’s production machine, and nothing would be recorded. The tool protects running deploys. When it gets a normal shutdown signal:

  1. It refuses to start any new deploy. If you press Deploy, a message asks you to try again in a few minutes.

  2. Every deploy already running continues until it finishes, up to a maximum wait.

  3. The tool stops only when every running deploy has ended, or when that wait runs out.

This does not affect the customer’s server. On the server, the SSH session that copies files or runs deploy.sh continues.

For administrators: the shortest timeout decides. Several deadlines apply, and each one ends with a forced kill. So the smallest one decides. If you raise only the tool’s own wait, nothing changes. With platform defaults, the wait is well under a minute. Then the protection does nothing, and nothing tells you. See the environment variables for the values to raise together.

A forced kill gives the tool no time to wait. Examples: out of memory, or a pod deleted with no grace period. A deploy killed this way is recorded as interrupted, not finished. The same three results from Retrying a failed server apply. Check what actually reached the server before you assume a clean failure.

The browser loses its connection

This section explains a more common case. The browser can lose its connection to the panel during a deploy. Examples: a network problem, or a closed laptop. The panel tries to reconnect by itself. It says Connection lost — reconnecting. The deploy keeps running. When the connection returns, the panel shows what happened in the meantime. You can also open the installation again: the panel goes back to the running deploy. You cannot start a second deploy of the same installation while one is running.