# Ubuntu / Coolify hosted deployment

This page shows a practical **reference deployment pattern** for running Crown on an **Ubuntu VPS** or a **dedicated Ubuntu server** with:

- **Coolify** on the target host
- **self-hosted Forgejo** on the infrastructure as the Git provider
- **a self-hosted OCI registry** for private image storage
- **a dedicated Coolify build server** for remote image builds
- **a delivery path of Forgejo → Coolify → build server → registry → runtime host**

This is intentionally different from the repo-inferred runtime topology. It explains a recommended production hosting shape rather than a graph that can be derived directly from source code alone.

If you want the other curated Coolify reference shapes, also see:

- [Single-server Coolify deployment](./single-server-coolify-deployment.md)
- [Multi-node Coolify + SurrealDB deployment](./multi-node-coolify-surrealdb-deployment.md)

Official references:

- [Coolify build server](https://coolify.io/docs/knowledge-base/server/build-server)
- [Coolify multiple servers](https://coolify.io/docs/knowledge-base/server/multiple-servers)
- [Coolify Docker registry](https://coolify.io/docs/knowledge-base/docker/registry)
- [Forgejo installation overview](https://forgejo.org/docs/latest/admin/installation/)
- [Forgejo installation with Docker](https://forgejo.org/docs/latest/admin/installation/docker/)
- [Forgejo container registry](https://forgejo.org/docs/latest/user/packages/container/)

## What “build server” means here

In this page, **build server** means Coolify's documented **Build Server** feature — not just any random CI box with Docker installed.

That means the build server is expected to be:

- added to Coolify as a server and marked as a **build server**
- able to access the application source code
- running **Docker Engine**
- authenticated with the target container registry
- using the **same CPU architecture** as the runtime servers

The Coolify docs also call out two important caveats:

- the built image must be pushed to a registry before deployment
- a server marked as a build server is effectively **build-only** until that option is disabled

If you configure multiple build servers, Coolify may select one at random, so keep them equivalent.

## Reference topology

![Ubuntu / Coolify hosted deployment](../diagrams/generated/ubuntu-coolify-deployment.svg)

Source: [`ubuntu-coolify-deployment.mmd`](../diagrams/generated/ubuntu-coolify-deployment.mmd)

## Flow order

![Ubuntu / Coolify deployment flow](../diagrams/generated/ubuntu-coolify-deployment-flow.svg)

Source: [`ubuntu-coolify-deployment-flow.mmd`](../diagrams/generated/ubuntu-coolify-deployment-flow.mmd)

This sequence view shows the end-to-end handoff order between Forgejo, Coolify, the dedicated build server, the registry, the migration job, the runtime host, and the live user request path.

## Why this pattern works well

The main idea is simple:

- **build somewhere else**
- **deploy prebuilt images to Ubuntu**
- **let Coolify handle rollout, routing, TLS, and environment management**

That gives you a cleaner production box because the deployed host is responsible for:

- pulling images
- starting/stopping containers
- storing runtime secrets and environment values
- routing HTTPS traffic
- preserving data volumes

Meanwhile, the dedicated Coolify build server is responsible for:

- cloning the source from Forgejo
- running the image build away from the runtime host
- pushing tagged images to the registry

If you also want broader CI gates like linting, integration tests, or policy checks, run those before the deployment trigger. The Coolify build-server feature is primarily about **offloading image builds** from the runtime host.

In other words: the production host behaves like a runtime, not like a build farm. Much healthier. Less drama.

## Main roles in the deployment

| Component                     | Responsibility                                                                         |
| ----------------------------- | -------------------------------------------------------------------------------------- |
| Self-hosted Forgejo           | Source of truth for code, PRs, and deployment-triggering webhooks                      |
| Coolify build server          | Dedicated build-only server used by Coolify to build images away from the runtime host |
| Self-hosted OCI registry      | Stores versioned images for Coolify to deploy                                          |
| Ubuntu VPS / dedicated server | Target runtime host                                                                    |
| Coolify                       | Manages apps, environment variables, rollouts, domains, and deploy triggers            |
| Reverse proxy                 | Terminates TLS and routes domains to the correct frontend or API                       |
| Runtime and delivery storage  | Stores operational data, support files, repositories, and image blobs                  |

## Typical deployment flow

1. A developer pushes to the self-hosted **Forgejo** instance or creates a release tag there.
2. A webhook or manual action triggers the resource in **Coolify**.
3. The resource is configured with `Use a Build Server?`, so Coolify dispatches the build to the dedicated build server instead of building on the runtime host.
4. The **Coolify build server** clones the source, builds the image, and pushes a versioned image to the **self-hosted OCI registry**.
5. Coolify then deploys that image to the **Ubuntu runtime host**.
6. The `database-migrations` job runs before dependent services come up.
7. Backend and frontend containers are recreated.
8. Traffic continues through the reverse proxy using the configured public domains.

## What runs on the Ubuntu host

A single Ubuntu VPS or dedicated server can host all of this on one machine:

- Forgejo
- the self-hosted registry
- Coolify control plane
- reverse proxy / ingress
- frontend containers
- backend service containers
- SurrealDB
- named volumes for persisted data

That is usually the simplest starting point.

For a larger dedicated setup, you often keep the same topology but give it more CPU, memory, and storage headroom. The logical layout does not really change — the box just sweats less.

The main catch is that the host is now serving both the **runtime** and the **delivery plane**, so repository growth and registry blob growth become operational concerns rather than somebody-else's problem.

## Recommended boundary between build and runtime

The Coolify build server should do the image-build work:

- source clone
- dependency install as needed by the image build
- Docker image creation
- image tagging
- registry authentication and image push

The Ubuntu/Coolify host should mainly do runtime work:

- image pull
- container restart / rollout
- env and secret injection
- TLS and domain routing
- volume mounting
- health-check-based recovery
- optionally hosting Forgejo and the registry if you accept the extra shared blast radius

That separation helps because it:

- reduces production-host load
- avoids compiler/toolchain drift on the runtime host
- makes deploys faster and more repeatable
- gives you cleaner rollback behaviour through image tags

## Network shape

The public internet should normally only reach:

- `80/443` for HTTP/HTTPS
- `22` for SSH, ideally restricted

Everything else should stay internal to Docker networking on the Ubuntu host.

A good baseline is:

- frontends and APIs behind the Coolify-managed proxy
- Forgejo and the registry also behind explicit domains if they are hosted on the same box
- SurrealDB not publicly exposed unless there is a very specific operational reason
- internal service-to-service traffic using container DNS names
- backups and admin access handled separately from user-facing traffic

## Data and persistence

In this pattern, the most important persisted runtime state is still:

- the **SurrealDB** volume
- the **support attachments** volume
- the **Forgejo** repository/config data
- the **registry blob** storage

If you are deploying on a VPS or dedicated host with local volumes, make sure you also have:

- scheduled backups
- restore testing
- disk monitoring
- enough free space for image pulls and log growth
- cleanup policies for stale images and old repository artifacts

## Single-host vs split-host options

### Single Ubuntu host

Best when you want the simplest path:

- Coolify, reverse proxy, services, and DB all on one VPS/dedicated server
- easiest operational model
- fewer moving parts
- good for early production or smaller workloads

If you want that model documented explicitly as a first-class reference pattern, use [Single-server Coolify deployment](./single-server-coolify-deployment.md).

### Split build + runtime

Best when you already have CI capacity elsewhere:

- Coolify build server handles image creation away from the runtime host
- Ubuntu/Coolify host only runs workloads
- better resource isolation
- usually the preferred pattern for stable production setups

### Split data host

Possible later if needed:

- app/runtime host remains on Ubuntu + Coolify
- database moves to a different machine or managed service
- requires more deliberate networking, backup, and latency planning

If you need full multi-server scale-out rather than only a split build/runtime path, continue with [Multi-node Coolify + SurrealDB deployment](./multi-node-coolify-surrealdb-deployment.md).

## How this fits Crown specifically

For Crown, this pattern maps well because:

- the repo already produces containerized frontends and services
- a migration job exists and should run before dependent services
- frontends are static-container deployments behind a proxy
- services expect internal DNS-based communication in production-style networking
- the platform already benefits from separating build concerns from runtime concerns
- Forgejo can act as the self-hosted Git system while the registry stays private and close to the runtime host

## Practical recommendation

If you are deploying Crown to Ubuntu with Coolify, the safest default is:

- **Coolify on the Ubuntu target host**
- **Forgejo as the self-hosted Git provider**, ideally with explicit storage and domain routing
- **dedicated Coolify build server** for remote image builds
- **self-hosted OCI registry** using immutable image tags
- **internal-only database networking**
- **persistent volumes** with backup coverage

That gives you a production flow that is understandable, repeatable, and much easier to reason about during incident response and rollback.
