> ## Documentation Index
> Fetch the complete documentation index at: https://ship.paralect.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Digital Ocean Apps

There is a simplified deployment type without Kubernetes. This type is **recommended** for most new applications because it allows you to set up infrastructure faster and doesn't require additional DevOps knowledge from the development team. You can switch to a more complex Kubernetes solution when your application will be at scale.

<Tip>
  Explore our method of deploying Ship to DigitalOcean Apps using [Infrastructure as Code](https://www.pulumi.com/what-is/what-is-infrastructure-as-code/).
  For a detailed guide, check out our [documentation on this approach](/docs/deployment/digital-ocean-apps-iac).
</Tip>

It's a step-by-step Ship deployment guide. We will use the [Digital Ocean Apps](https://www.digitalocean.com/products/app-platform) and [GitHub Actions](https://github.com/features/actions) for automated deployment. A [DigitalOcean Managed PostgreSQL](https://www.digitalocean.com/products/managed-databases-postgresql) database and [Redis Cloud](https://redis.com/try-free/) for data storage, and [Cloudflare](https://www.cloudflare.com/) for DNS and SSL configuration.

You need to create [GitHub](https://github.com/), [Digital Ocean](https://www.digitalocean.com/), [CloudFlare](https://www.cloudflare.com/) and [Redis Cloud](https://redis.com/try-free/) accounts.

Also, you need [git](https://git-scm.com/) and [Node.js](https://nodejs.org/en/) if you already haven't.

<Note>
  [Migrator](/docs/migrator) (Drizzle migrations) and [Scheduler](/docs/scheduler) run from the API image inside the same application. Unlike [Kubernetes](https://github.com/docs/deployment/kubernetes/digital-ocean.md), where separate containers are used for them.
</Note>

## Setup project

First, initialize your project. Type `npx create-ship-app init` in the terminal then choose desired build type and **Digital Ocean Apps** as a cloud service provider.

```bash theme={null}
npx @paralect/ship init
```

You will have next project structure.

```shell theme={null}
/my-app
  /apps
    /web
    /api
  /.github
  ...
```

Create GitHub private repository and upload source code.

<img src="https://mintcdn.com/ship/NJ8GmOFvTnU_w738/images/private-repo.png?fit=max&auto=format&n=NJ8GmOFvTnU_w738&q=85&s=99e9c308e537fa2147b9a42e45851b4a" alt="Private repo" width="3024" height="1664" data-path="images/private-repo.png" />

```shell theme={null}
cd my-app
git remote add origin https://github.com/Oigen43/my-app.git
git branch -M main
git push -u origin main
```

## PostgreSQL

Ship runs on PostgreSQL via Drizzle ORM. DigitalOcean offers a managed PostgreSQL database you can provision next to your app.

### Database creation

1. In the [DigitalOcean Control Panel](https://cloud.digitalocean.com/), open the **Databases** tab and click `Create Database Cluster`.
2. Select **PostgreSQL** as the database engine.
3. Choose a region. We recommend the same region as your App Platform application to keep latency low.
4. Select a plan. A basic single-node plan is enough for staging/demo environments. For production, pick a plan with standby nodes and automated backups.
5. Name the cluster and click `Create Database Cluster`.

### Connection

After the cluster is provisioned, open its **Overview** page and find the **Connection Details** section. Switch the connection string format to `Connection string` and copy it. This is your `DATABASE_URL` value in the form `postgresql://user:password@host:port/dbname?sslmode=require`.

Under **Settings → Trusted Sources**, add your App Platform app so the API can reach the database.

Save this value. It will be needed later when creating the app in Digital Ocean.

<Tip>
  Before moving to production, enable automated backups on your PostgreSQL cluster.

  This ensures that you can reliably restore your data in the event of unforeseen circumstances.
</Tip>

## Redis Cloud

Navigate to [Redis Cloud](https://redis.com/try-free/) and create an account. Select cloud provider and region, then press `Let's start free` to finish database creation.

<img src="https://mintcdn.com/ship/NJ8GmOFvTnU_w738/images/redis-creation.png?fit=max&auto=format&n=NJ8GmOFvTnU_w738&q=85&s=a45dc74ec5ef9ab0fe1dba7e41b0cbf3" alt="Redis create database" width="953" height="581" data-path="images/redis-creation.png" />

Open database settings and get the database public endpoint and password.

<img src="https://mintcdn.com/ship/NJ8GmOFvTnU_w738/images/redis-public-endpoint.png?fit=max&auto=format&n=NJ8GmOFvTnU_w738&q=85&s=c9ea4f3ceed66d3150be43660aed728f" alt="Redis public endpoint" width="1601" height="923" data-path="images/redis-public-endpoint.png" />

<img src="https://mintcdn.com/ship/NJ8GmOFvTnU_w738/images/redis-password.png?fit=max&auto=format&n=NJ8GmOFvTnU_w738&q=85&s=1914c1c31a6028b74820e10b3151da03" alt="Redis password" width="1573" height="514" data-path="images/redis-password.png" />

Form Redis connection string using public endpoint and password `redis://:<password@<public-endpoint>`. Save this value. It will be needed later when creating the app in Digital Ocean.

## Digital Ocean

Navigate to the Digital Ocean Control Panel and select the **Apps** tab. The `Full-Stack` build type requires 2 applications. First for the [Web](/docs/web/overview) (TanStack Start) app and second for the [API](/docs/api-reference/overview), plus the Migrator and Scheduler services that run from the same API image.

### Initial step

1. Select GitHub as a service provider. You might need to grant Digital Ocean access to your GitHub account or organization.
2. Select the repository with the application.
3. Select a branch for deployment.
4. Select the source directory if the code is in a subfolder.It should `apps/web` for web application and `apps/api` for api.
5. Turn off the Autodeploy option. The Ship uses GitHub Actions for CI due to the poor support of monorepos in the Digital Ocean Apps

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-app.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=a36b63f3aa9bea9752427e2710fb31d8" alt="Create app screen" width="1213" height="1171" data-path="images/do-create-app.png" />

### Resources setup

1. Delete duplicated resources without dockerfile if you have one.
2. Select desired plan. For staging/demo environments, sufficiently selecting a basic plan for 5\$. For production, you might consider selecting a more expensive plan.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-app-step-2.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=ac5ad7f32b4dc3ec49f4a7865fe6b0b8" alt="Create app resources" width="1219" height="781" data-path="images/do-create-app-step-2.png" />

### Environment variables

The `APP_ENV` environment variable is typically set based on the environment in which the application is running.
Its value corresponds to the specific environment, such as "development", "staging" or "production".
This variable helps the application identify its current environment and load the corresponding configuration.

For the web application, by setting the environment variable `APP_ENV`,
the application can determine the environment in which it is running and load the appropriate configuration file:

| APP\_ENV    | File             |
| ----------- | ---------------- |
| development | .env.development |
| staging     | .env.staging     |
| production  | .env.production  |

These files hold the client config for each environment. The web app is built with Vite, so client-side variables are prefixed with `VITE_` and read through `import.meta.env` (for example `VITE_API_URL`, `VITE_WS_URL`, `VITE_WEB_URL`). They are baked in at build time, so commit up-to-date values before you deploy.

In contrast, the API utilizes a single `.env` file that houses its environment-specific configuration.
This file typically contains variables like API keys, secrets, or other sensitive information.
To ensure security, it's crucial to add the `.env` file to the `.gitignore` file,
preventing it from being tracked and committed to the repository.

So just specify the environment variables that will contain the values of your secrets.
For example, if you have a secret named `API_KEY`,
create an environment variable named `API_KEY` and set the value of the corresponding secret for it. The API needs at least `DATABASE_URL` (your PostgreSQL connection string) and `REDIS_URI` (your Redis connection string).

Variables added in the `Global` section will be available to all resources within the application, while ones added in the `ship` section will be available only for that resource. Adding `DATABASE_URL` and `REDIS_URI` in the global section lets the migrator and scheduler resources reuse them later.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-app-step-3.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=2d4e2d17c1761b315a4f2e12b7d32135" alt="Create app environment variables" width="1207" height="1213" data-path="images/do-create-app-step-3.png" />

### Application name and hosted region

* \[**Optional**] Select desired application name and/or region for your application

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-app-step-4.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=049bb812c567bd93d292084fd199c2d0" alt="Create app host" width="1223" height="760" data-path="images/do-create-app-step-4.png" />

### Review

Verify everything is correct and create a new resource.

After the application creation, you'll land on the application dashboard page. On dashboard, you can see application status, check runtime logs, track deployment status, and manage application settings.

### App Spec

Digital Ocean sets the path to Dockerfiles to the root by default. You will need to change it manually.
Navigate to Settings, expand the App spec tab and change `dockerfile_path` in the editor.

To deploy your application in a monorepo, it's essential to modify the `source_dir` parameter to the root directory.
This adjustment is necessary to ensure the correct configuration and operation of the applications within the monorepo.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-settings-app-spec.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=baf08be526b34a2485397face655ebcd" alt="Create app review" width="2296" height="1378" data-path="images/do-settings-app-spec.png" />

## Cloudflare

Before this step you need to register a domain name, usually we already have it if not, look: [Register a new domain](https://developers.cloudflare.com/registrar/get-started/register-domain)

Navigate to your Digital ocean application and open `Settings` tab. Select `Domains` row to open domain settings and click `Add domain` button

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-domains.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=2d910dfc0242da084289febdd7a66280" alt="Digital Ocean domains" width="1260" height="890" data-path="images/do-domains.png" />

Type your desired domain and select option `You manage your domain`

In the bottom section you'll be asked to copy CNAME alias of your digital ocean application name to record in your dns provider.
Copy that alias and leave the form (do no close it or submit).

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-new-domain.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=355fe3893378350db0cee9214a10b302" alt="Digital Ocean new domain" width="1008" height="921" data-path="images/do-new-domain.png" />

Navigate to [CloudFlare](https://dash.cloudflare.com/) and sign into account.

1. Go to `DNS` tab and create a new record.
2. Click `Add record`. Select type `CNAME`,  enter domain name (must be the same you entered in digital ocean settings) and paste alias into `target` field.
   Make sure `Proxy status` toggle enabled.
3. Save changes

<img src="https://mintcdn.com/ship/x4usuONuzojy0ONL/images/cloudflare-create-dns.png?fit=max&auto=format&n=x4usuONuzojy0ONL&q=85&s=08d922180734e89d7f207cc1e53db0c0" alt="Cloudflare DNS" width="1043" height="404" data-path="images/cloudflare-create-dns.png" />

Now go back to digital ocean and submit form. It usually takes about 5 minutes for digital ocean to confirm and start using your new domain.
Once domain is confirmed, application can be accessed by new address.

## GitHub Actions

You can find two github actions in the `.github/workflows` folder, responsible for triggering deployment when you push changes in your repository. If you chose frontend or backend on the initialization step, you'll have one github workflow for the selected application type.

These actions require a Digital Ocean access token and application ID. Respectively these are `DO_ACCESS_TOKEN` and `DO_API_STAGING_APP_ID`/`DO_WEB_STAGING_APP_ID`/`DO_API_PRODUCTION_APP_ID`/`DO_WEB_PRODUCTION_APP_ID`.

Navigate to digital ocean and open the **API** tab on the left sidebar.
Click **Generate new token**, select a name and set the expiration period.
Also, pick both **read** and **write** permissions for the scope.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-access-token-create.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=98e28f15c104309727caefc9cf8eab99" alt="Do access token create" width="1813" height="561" data-path="images/do-access-token-create.png" />

You'll see generated token in the list. Do not forget to copy the value and store it in a safe place. You won't be able to copy value after leaving the page.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-access-token-copy.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=42c16aecebe4f6dad0cfc8892aef94e9" alt="Do access token copy" width="1809" height="637" data-path="images/do-access-token-copy.png" />

Next, navigate to the **Apps** tab in the left sidebar and open your Digital Ocean application. You can find the id of your application id in the browser address bar.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-application-id.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=0b475c34404e66865d8196e2473df358" alt="Do application id" width="2293" height="726" data-path="images/do-application-id.png" />

Now you can add these keys to your github repository's secrets.

Navigate to the GitHub repository page, and open the **Settings** tab and these values. You have to be repository **admin** or **owner** to open this tab.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/github-secrets.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=b4161e1be3970b5b8f5f01220318e359" alt="Github secrets" width="1418" height="922" data-path="images/github-secrets.png" />

Done! Application deployed and can be accessed by provided domain.

<img src="https://mintcdn.com/ship/x4usuONuzojy0ONL/images/deployed-application.png?fit=max&auto=format&n=x4usuONuzojy0ONL&q=85&s=f712611f5b52dfcce754a24229ab08d6" alt="Deployed application" width="1134" height="705" data-path="images/deployed-application.png" />

## Set up migrator and scheduler (Optional)

Digital Ocean Apps allows configuring additional resources within one application, which can serve as background workers and jobs, and a scheduler to run before/after the deployment process.

The migrator is a pre-deploy `Job` that applies Drizzle migrations (`pnpm --filter api migrate`, built from `apps/api/Dockerfile.migrator`), and the scheduler is a `Worker` that runs background jobs (`pnpm --filter api schedule`, built from `apps/api/Dockerfile.scheduler`). Both reuse the API image and the global `DATABASE_URL` and `REDIS_URI` variables.

Navigate to your Digital Ocean application. **Make sure to select the application with API server**, open a `Create` dropdown menu in the top-right corner, and select the `Create Resources From Source Code` option.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-resource.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=ddcaf8e09d7584f7bb36bb1d880b3a15" alt="Do create resource" width="1153" height="588" data-path="images/do-create-resource.png" />

1. Select a project repository, add a path to the source directory, disable auto-deploy, and press `Next`.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-resource-form.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=3479c9f580f0c10d20712e7e3b84c516" alt="Create resource screen" width="900" height="1124" data-path="images/do-resource-form.png" />

2. Delete a resource without Dockerfile and edit second by clicking on the pencil icon.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-resource-step-2.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=2e6802b4ab3e7cd252f65bf596920c21" alt="Create app resources" width="1219" height="781" data-path="images/do-create-resource-step-2.png" />

3. In the edit resource form, select `Resource Type` - `Job`, `Before every deploy`, and change the name of the resource (not required, but might be useful later). Press save and go back to the resources screen.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-resource-type.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=d3aad21ff7a7de1d53cbc0862a7f8342" alt="Edit resource screen" width="866" height="1114" data-path="images/do-resource-type.png" />

4. Select the `Add Additional Resource from Source` option below the list of added resources, repeat steps 1-2, and navigate to the edit form for a new resource.

5. Select `Resource Type` - `Worker`, save changes and go back.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-resource-type-2.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=c483a5fcc2ad56c974900416312a591b" alt="Edit resource screen" width="865" height="519" data-path="images/do-resource-type-2.png" />

6. Proceed with the next steps, add environment variables if needed, verify a new monthly cost of the application and create resources.

You can find created resources in the `overview` tab.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-resources-overview.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=c1a9e6f5e4cbab662f3b113d141366b9" alt="Resources overview screen" width="1207" height="635" data-path="images/do-resources-overview.png" />

7. Navigate to Application Spec `(settings tab)`. Change the `dockerfile_path` variable to files with migrator and scheduler.
   Migrator is placed in the `jobs` section. You can also find it by name of the resource. The scheduler is placed in the `workers` section.

<Note>
  To deploy your application in a monorepo, it’s essential to modify the `source_dir` parameter to the root directory.
  This adjustment is necessary to ensure the correct configuration and operation of the applications within the monorepo.
</Note>

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-migrator-app-spec.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=9ea6ca894747db1110f4531d2a67ac7a" alt="Migrator spec screen" width="1051" height="599" data-path="images/do-migrator-app-spec.png" />

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-scheduler-app-spec.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=ad94adf46e13b639f18552aa31c990de" alt="Scheduler spec screen" width="1067" height="602" data-path="images/do-scheduler-app-spec.png" />

## Logging (optional)

### Build-in

Digital Ocean has built-in logs in raw format. It will gather all data that your apps will produce.
In order to view them, follow these steps:

1. Log in to your Digital Ocean account.
2. Click on the Apps tab in the left-hand navigation menu.
3. Click on the name of the app you want to view the logs for.
4. Click on the Runtime Logs tab in the app dashboard.
5. You will see a list of logs for different components of your app. Click on the component you want to view the logs for.
6. You can filter the logs by time, severity, and component. Use the drop-down menus provided to select your filter criteria.
7. You can also search for specific keywords in the logs by using the search bar at the top of the page.

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-runtime-built-in-logs.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=e9d09591231ea08085e492d963050a68" alt="Runtime built in logs screen" width="3002" height="1420" data-path="images/do-runtime-built-in-logs.png" />

### Integrations

Currently, Digital Ocean Apps supports only 3 integrations: [PaperTrail](https://marketplace.digitalocean.com/add-ons/papertrail), [Logtail](https://marketplace.digitalocean.com/add-ons/logtail) and [Datadog](https://www.datadoghq.com/). You can find detailed instructions on how to set up these logs at this [link](https://docs.digitalocean.com/products/app-platform/how-to/forward-logs/).

### Example Integration Logtail

To configure Logtail follow these steps:

1. Create account on Logtail
2. Open Sources on the left sidebar.
3. Create new source by clicking "Connect source" button

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-logs-logtail-sources.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=75371fc2c4acf1c51184925bee934001" alt="Logs Integrations logtail sources" width="2782" height="1452" data-path="images/do-logs-logtail-sources.png" />

4. Select HTTP source and specify name for this connection

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-logs-logtail-connect-source.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=1417566973048b00fd1e47a6bc8caad9" alt="Logs Integrations Logtail connect" width="1157" height="637" data-path="images/do-logs-logtail-connect-source.png" />

5. Copy source token

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-logs-logtail-token.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=1a90cc7a5f9f868637d11756c8d06fd3" alt="Logs Integrations Logtail token" width="1203" height="591" data-path="images/do-logs-logtail-token.png" />

6. Open Digital Ocean Apps
7. Select Settings tab for your application

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-app-logs-settings.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=ea084f54ffbc687f72569fd9232bedb4" alt="Logs Integrations Settings" width="1500" height="540" data-path="images/do-app-logs-settings.png" />

8. Select Log Forwarding and then press "Add Destination"

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-logs-log-forwarding.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=c8693b31f3ab7b308ebbc62002e4b440" alt="Logs Forwarding" width="1256" height="248" data-path="images/do-logs-log-forwarding.png" />

9. Fill information with token that we retrieved from Logtail

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-create-log-forward.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=95cc282482a69f90cb571e987419a77e" alt="Logs Create Log Forward" width="794" height="749" data-path="images/do-create-log-forward.png" />

10. That's it! In couple minutes your app will send the latest logs to Logtail

<img src="https://mintcdn.com/ship/aSd7_VT7diYXG7bS/images/do-logs-logtail-final-view.png?fit=max&auto=format&n=aSd7_VT7diYXG7bS&q=85&s=08cbed4c391bdce857972ec3cc17d1c4" alt="Logs Logtail Final View" width="1216" height="550" data-path="images/do-logs-logtail-final-view.png" />
