# deploy the app
kamal deploy
```

If deploy continues to fail, check the logs of your docker container, using:

```bash
kamal app logs
```

#### Resolving `ERROR exec /bin/sh: exec format error`

[Section titled “Resolving ERROR exec /bin/sh: exec format error”](#resolving-error-exec-binsh-exec-format-error)

If you see this error on your server/logs it is likely that the architecture used to build your image is not the same as the one running on your server.

Review the `builder` section of your `deploy.yml` file and in particular make sure `multiarch` is set to `true`. You can also explicitly build the image on the remote server, or set the target architecture using other `builder` options as described [in the kamal docs](https://kamal-deploy.org/docs/configuration#using-remote-builder-for-native-multi-arch).

#### Resolving `ERROR /bin/sh: 1: /start: not found`

[Section titled “Resolving ERROR /bin/sh: 1: /start: not found”](#resolving-error-binsh-1-start-not-found)

If you see this error on your server/logs it is likely that your `/start` script has the wrong line endings. This can happen if you edit the `./deploy/docker_startup.sh` file in certain programs on the Windows operating system.

To fix this, change the line endings of the file from CRLF to LF using your preferred text editor (you can Google or ask ChatGPT how to do this for your specific environment).

#### Health checks are failing because of `ALLOWED_HOSTS`

[Section titled “Health checks are failing because of ALLOWED\_HOSTS”](#health-checks-are-failing-because-of-allowed_hosts)

Kamal runs a “health check” during deploys to ensure your new application is ready to handle requests. This involves pinging your workers on an internal docker address and waiting for them to respond with a “200 OK” status code.

Because it’s not possible to predict the hostname for these requests, a special middleware that bypasses the host check is included in Pegasus to handle this situation.

If health checks are failing that means the middleware isn’t set up properly. The healthcheck url that kamal hits (`/up` by default), must match the path defined in `apps.web.middleware.healthchecks.HealthCheckMiddleware` (also `/up` by default).

To fix it, confirm the following things:

1. Ensure that `"apps.web.middleware.healthchecks.HealthCheckMiddleware",` is the first middleware in `settings.MIDDLEWARE`.

2. Ensure that the paths match.

   1. The kamal path is defined in the `proxy` section of `deploy.yml`, under `healthcheck`. If there is no `healthcheck` section, then it’s using the default path.
   2. The middleware path is defined in `apps/web/middleware/healthchecks.py`.

### Cookbooks

[Section titled “Cookbooks”](#cookbooks)

#### Changing your site URL

[Section titled “Changing your site URL”](#changing-your-site-url)

To change your site’s URL, do the following:

1. Set up a new DNS endpoint as outlined above.
2. Change the `host` value in your proxy configuration in `deploy.yml` to the new domain.
3. Update your `ALLOWED_HOSTS` setting / environment variable as needed.
4. Run `kamal proxy reboot`.
5. Run `kamal deploy`

Your app should now be running on your new domain.

#### Getting a database backup

[Section titled “Getting a database backup”](#getting-a-database-backup)

Here is one way to get a database dump of your server:

First you can run the following command to save a database dump to the *host* machine:

```bash
kamal accessory exec postgres 'pg_dump -h localhost -p 5432 -U <your_app_user> <your_app_db_name> > db_dump.sql' --reuse
```

This should create a file on the *host* machine at `/home/kamal/db_dump.sql`.

If you want to copy this file locally, you can run:

```bash
scp kamal@yourapp.com:db_dump.sql ./
```

Note: you may want to zip or gzip this file first if you have a large database.

#### Doing a database restore

[Section titled “Doing a database restore”](#doing-a-database-restore)

To restore a database you first put the backup file on the host:

```bash
scp ./db_dump.sql kamal@yourapp.com:
```

Then create the DB:

```bash
kamal accessory exec postgres 'createdb -h localhost -p 5432 -U <your_app_user> <your_app_db_name>' --reuse
```

After that you will need to login to the *host* machine:

```bash
ssh kamal@yourapp.com
```

And copy the database dump onto the DB machine. Run `docker ps` to get the container id of the DB machine. Then run:

```bash
docker cp db_dump.sql <CONTAINER ID>:/tmp/db_dump.sql
```

Finally, login to the DB container:

```bash
docker exec -it <CONTAINER ID> /bin/bash
```

And restore the data:

```bash
psql -h localhost -p 5432 -U <your_app_user> <your_app_db_name> < /tmp/db_dump.sql
```

#### Deploying multiple apps to the same server

[Section titled “Deploying multiple apps to the same server”](#deploying-multiple-apps-to-the-same-server)

One of the major benefits of the VPS-based approach is that you can easily host multiple apps on the same hardware, which is usually a substantial cost advantage over hosting each one on its own.

This is now supported out of the box by Kamal and Pegasus. To deploy multiple applications to the same server, just set up Kamal individually for each application and run through the steps above. Once multiple sites are set up, `kamal-proxy` will automatically route traffic to the right app based on the site URL.

#### Running Docker as a non-root user

[Section titled “Running Docker as a non-root user”](#running-docker-as-a-non-root-user)

Follow these steps if you don’t want to run kamal and Docker as the root user.

##### Manually Install Docker

[Section titled “Manually Install Docker”](#manually-install-docker)

If you don’t run kamal as root you’ll have to install Docker yourself.

You can test if Docker is installed by running `docker -v` on the command line. You should see output like the following if it is installed correctly.

```plaintext
Docker version 24.0.5, build 24.0.5-0ubuntu1~20.04.1
```

If you need to install it, you can find instructions in [Docker’s documentation](https://docs.docker.com/engine/install/ubuntu/). You only need to install Docker Engine, not Docker Desktop.

##### Prepare a user account for Kamal

[Section titled “Prepare a user account for Kamal”](#prepare-a-user-account-for-kamal)

Next, create a user for Kamal to use. You can choose any username you like. In this example we will use `kamal`. We’ll also add this user to the `docker` group so that Kamal can run docker commands.

First login to your server as a user with root access. Then run the following commands:

```bash
sudo adduser kamal --disabled-password
sudo adduser kamal --add_extra_groups docker
```

Next, add your SSH key to the `kamal` user’s `authorized_keys` file so you can login without a password. If you need to generate an SSH key you can [follow these steps](https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server):

```bash
sudo mkdir -p /home/kamal/.ssh
sudo cp ~/.ssh/authorized_keys /home/kamal/.ssh/authorized_keys
sudo chown -R kamal:kamal /home/kamal/.ssh
```

Next, test the login works. Exit out of your server and on your *local machine* run:

```bash
ssh kamal@<ip-address>
```

If you’ve set everything up properly the `kamal` user should be able to login with no password.

Once you’re logged in, as a final test, ensure the `kamal` user can run docker commands by running:

```bash
docker run hello-world
```

If the command above completes without error you are ready to go!

Finally, update your `config/deploy.yml` file to specify a different user by adding an ssh section, as [described in the docs](https://kamal-deploy.org/docs/configuration/ssh/#the-ssh-user):

```yaml
ssh:
  user: kamal
```

#### Cleaning up old Images on Amazon ECR

[Section titled “Cleaning up old Images on Amazon ECR”](#cleaning-up-old-images-on-amazon-ecr)

Amazon ECR charges based on the amount of image storage you use. In order to keep costs down, you set a lifecycle policy that cleans up old images.

The following configuration can be used to clean up all but the most recent 3 images:

![ECR Lifecycle Policy](/_astro/ecr-lifecycle-rule.BOc8NbC2_1XcA2R.webp)

For more details, see [Amazon’s documentation on lifecycle policies](https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html)