docs: add 'Deploy an app' how-to guide, init 'Image tag format' reference

This commit is contained in:
Pasha Sviderski
2025-11-12 15:54:05 +10:00
parent 90d244fea8
commit 7ebcba7c36
2 changed files with 437 additions and 0 deletions
+434
View File
@@ -0,0 +1,434 @@
# Deploy an app
Deploy a containerised application to your Uncloud cluster using either source code or pre-built images.
This guide covers both scenarios:
- **[Deploy from source code](#deploy-from-source-code)**: Build Docker images from your application code and use them
to deploy service containers
- **[Deploy pre-built images](#deploy-pre-built-images)**: Deploy service containers using existing images from a
registry or your local machine
Uncloud uses [Compose Specification](https://compose-spec.io/) for defining the deployment configuration of your app's
services. It implements the most common Compose features with some Uncloud-specific extensions. See
[Compose support matrix](../8-compose-file-reference/1-support-matrix.md) for details.
## Prerequisites
- `uc` CLI [installed](../2-getting-started/1-install-cli.md) on your local machine
- An Uncloud cluster with at least one machine (see [Quick start](../2-getting-started/2-deploy-demo-app.md))
- Basic knowledge of [Compose Specification](https://compose-spec.io/)
## Deploy from source code
Use this scenario when you want to build and deploy your application from
a [Dockerfile](https://docs.docker.com/reference/dockerfile/) and source code available on your local machine.
### 1. Create a Compose file
Create a `compose.yaml` file in your application directory with a
[`build`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#build) section for each service you want to
build. Here is a minimal example of a Compose file that builds and deploys a web app that consists of a single service
called `web`:
```yaml title="compose.yaml"
services:
web:
# Build an image from the Dockerfile in the current directory
build: .
# Publish the container port 8000 as https://app.example.com
x-ports:
- app.example.com:8000/https
```
If you don't have a Dockerfile for building an image from your source code, create one in the same directory.
### 2. Build and deploy your app
To build and deploy services defined in your Compose file, navigate to the directory with `compose.yaml` and run:
```shell
uc deploy
```
This command looks for a Compose file in your current working directory and:
1. **Builds images** for services with a `build` section using your local Docker and tags them with the current Git
version
2. **Pushes built images** directly **to cluster machines** using
[unregistry](https://github.com/psviderski/unregistry), transferring only the missing layers
3. **Plans the deployment** and shows you what will change, asking for confirmation
4. **Creates any missing volumes** on target machines
5. **Deploys service containers** using the built images and latest configuration changes with zero-downtime rolling
updates
See the [uc deploy](../9-cli-reference/uc_deploy.md) reference for all available options.
Watch `uc deploy` building and deploying a demo app (18 seconds):
<video controls width="100%">
<source src="https://media.uncloud.run/docs/uc-deploy-demo.mp4"/>
</video>
### Customise the build configuration
If you need more control over the build configuration, you can specify additional attributes in the `build` section of
your Compose file. For example, to specify a custom Dockerfile location, set build-time arguments, or build
multi-platform images.
```yaml
services:
web:
build:
# Relative path to the directory with your Dockerfile
context: ./backend
# Set build-time variables defined as ARG in your Dockerfile
args:
ALPINE_VERSION: 3.22
BUILD_ENV: prod
# Build a multi-platform image
platforms:
- linux/amd64
- linux/arm64
```
You can pass additional build arguments or override existing ones using the `--build-arg` flag with `uc deploy`:
```shell
uc deploy --build-arg BUILD_ENV=dev
```
You can also use advanced features like build caches, secrets, and SSH access.
See [Compose Build Specification](https://github.com/compose-spec/compose-spec/blob/main/build.md) for all supported
attributes.
:::info note
To build multi-platform images, you need to configure your local Docker to use the
[containerd image store](https://docs.docker.com/desktop/features/containerd/).
:::
### Customise image tags
If you don't specify the `image` attribute, `uc deploy` tags built images with a Git-based version like
```yaml
# <project>/<service>:<git datetime>.<short git sha>
myapp/web:2025-10-30-223604.84d33bb
```
It uses your local date/time if the working directory is not a Git repository.
You can customise the image name and tag format using the `image` attribute. It can be a static name or a dynamic
template using [environment variables](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation) and
the [Go template](https://pkg.go.dev/text/template) syntax.
```yaml
services:
web:
build: .
# Custom image name and tag format
image: myapp:{{gitdate "20060102"}}.{{gitsha 7}}.${GITHUB_RUN_ID:-local}{{if .Git.IsDirty}}.dirty{{end}}
```
This generates tags like:
- `myapp:20251030.84d33bb.local.dirty` when building locally with uncommitted changes
- `myapp:20251030.84d33bb.1234` when building in CI with `GITHUB_RUN_ID=1234` and a clean repo
`uc deploy` renders the image templates when it loads the Compose file and then uses the resulting names for the build
and deploy stages.
See the [Image tag format](../8-compose-file-reference/2-image-tag-format.md) reference for all available template
variables and functions.
### Separate build and deploy steps
You might want to build and deploy services as separate commands. For example, to run them as separate steps in your
CI/CD pipeline or have more control over the build and deploy process. Here are the commands that are equivalent to
`uc deploy`:
```shell
# Build images and push to cluster machines
uc build --push
# Deploy services using the built images
uc deploy --no-build
```
### Deploy configuration changes only
You can use the `--no-build` flag with `uc deploy` to deploy only the configuration changes in your Compose file if your
source code and images haven't changed. However, the image tag may still change if you're using
[dynamic tags](#customise-image-tags) based on the Git state (default). In that case, the deploy will likely fail
because the new tag won't be found on cluster machines.
Use less sensitive dynamic tags or specify the built and pushed images you want to deploy explicitly to avoid this
issue.
The recommended approach though is to commit all your changes, including the configuration ones, to the repo. Then
rebuild and deploy your services from a clean repo state. This way, the image tags will always reflect the exact source
code and configuration used for the deployment.
## Deploy pre-built images
Use this scenario when you want to deploy your application using pre-built images from a container registry (for
example, Docker Hub or GitHub Container Registry) or your local Docker.
### 1. Create a Compose file
Create a `compose.yaml` file that references an image from a registry using the
[`image`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) attribute. It's important that you don't
include a `build` section for services using pre-built images. Here is a minimal example of a Compose file that deploys
an app that consists of a single `nginx` service:
```yaml title="compose.yaml"
services:
nginx:
# Use the nginx image from Docker Hub
image: nginx:latest
# Publish the container port 80 as https://nginx.example.com
x-ports:
- nginx.example.com:80/https
```
### 2. Deploy your app
To deploy services defined in your Compose file, navigate to the directory with `compose.yaml` and run:
```shell
uc deploy
```
This command looks for a Compose file in your current working directory and:
1. **Plans the deployment** and shows you what will change, asking for confirmation
2. **Creates any missing volumes** on target machines
3. **Pulls images** from a registry on cluster machines where services are deployed according to the
[`pull_policy`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#pull_policy)
4. **Deploys service containers** using the pulled images and latest configuration changes with zero-downtime rolling
updates
### Control image pulling
By default, `uc deploy` pulls an image from a registry only if it's missing on a target machine. You can change this
behavior using the [`pull_policy`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#pull_policy)
attribute. For example, to always pull the latest version of an image before deploying.
```yaml
services:
nginx:
image: nginx:alpine
# Always pull the latest :alpine tag before deploying
pull_policy: always
```
Available `pull_policy` values:
- `always`: Always pull the image from the registry before deploying
- `missing` (default): Pull only if the image isn't available on the target machine
- `never`: Never pull, the image must be present on the target machine or the deploy will fail
### Pull from a private registry
If your images are in a private registry, `uc deploy` needs an authentication token to pull them. You can provide it by
either:
- Logging in to the registry using your local Docker (recommended)
- Logging in to the registry using Docker on each cluster machine you want to deploy to
When you're logged in using your local Docker, `uc deploy` automatically passes your local Docker credentials for the
private registry to cluster machines when pulling images. This way, you don't need to log in on each machine manually.
See [`docker login`](https://docs.docker.com/reference/cli/docker/login/) for instructions on how to log in to a private
registry.
### Push local images to cluster machines
You can also deploy pre-built images that exist only in your local Docker and are not available for pulling on cluster
machines. For example, images you built locally outside of the Compose workflow or pulled from a private registry that
is unreachable from your cluster machines. This is useful for deploying to air-gapped or restricted environments.
You can push these local images directly to your cluster machines using the
[`uc image push`](../9-cli-reference/uc_image_push.md) command:
```shell
# Push to *all* cluster machines
uc image push myapp:latest
# Push to specific machines only
uc image push myapp:latest -m machine1,machine2
```
This command uploads the image from your local Docker to the cluster machines using
[unregistry](https://github.com/psviderski/unregistry) running as part of the Uncloud daemon on each machine. It
efficiently transfers only the image layers that don't already exist on the target machines.
After pushing the image, update your Compose file to reference it:
```yaml
services:
web:
# Reference the image you just pushed
image: myapp:latest
# Never try to pull from a registry since the image is only available locally
pull_policy: never
```
Run [`uc images`](../9-cli-reference/uc_images.md) to verify that the image is available on the target machines.
Then deploy as usual:
```shell
uc deploy
```
:::tip
Set `pull_policy: never` when using local images to prevent `uc deploy` from trying to pull them from a registry.
:::
## Mix source builds and pre-built images
Define multiple services in your Compose file, mixing source builds and pre-built images:
```yaml
services:
web:
# Build web service from source
build: .
x-ports:
- app.example.com:8000/https
db:
# Pull a pre-built PostgreSQL image from Docker Hub
image: postgres:18
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
```
`uc deploy` builds and pushes images for services with a `build` section and pulls images from a registry for services
with only an `image` attribute.
:::warning
**Service names must be globally unique** across all Compose files deployed to the same cluster.
Unlike Docker Compose or Docker Swarm, Uncloud doesn't automatically prefix service names with project or stack names.
Choose unique names to avoid conflicts with services deployed from other Compose files.
:::
## Deploy to specific machines
By default, Uncloud randomly chooses available machines to run your services on, spreading multiple replicas of a
service across all machines for high availability. You can restrict which machines can run your service using the
[`x-machines`](../8-compose-file-reference/1-support-matrix.md#x-machines) extension in your Compose file.
This is useful when you want to:
- Deploy services to machines in a specific region or data center
- Run services only on machines with specific hardware (for example, GPUs or ARM processors)
- Keep certain services isolated to dedicated machines
- Deploy to a subset of machines for testing before rolling out cluster-wide
Here's a basic example:
```yaml
services:
web:
build: .
x-ports:
- app.example.com:8000/https
# Spread 3 replicas across machine-1 and machine-2 only
scale: 3
x-machines:
- machine-1
- machine-2
db:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql
# Create the db-data volume and run the DB container on machine-db
x-machines: machine-db
volumes:
db-data:
```
When you deploy this Compose file with `uc deploy`:
- The `web` service will create and spread its 3 replicas only across `machine-1` and `machine-2`
- The `db` service will create its `db-data` volume and run only on `machine-db`
:::tip
Use [`uc machine ls`](../9-cli-reference/uc_machine_ls.md) to see available machines in your cluster.
:::
### Push images to specific machines only
When building from source, `uc deploy` and `uc build --push` automatically push images to **all** cluster machines by
default. This ensures images are available wherever services might be deployed.
If you're using `x-machines` to restrict deployments to specific machines, `uc deploy` and `uc build --push` push images
only to those machines. This saves time and bandwidth by uploading images only where they're needed.
## Run one replica on each machine
To deploy exactly one service replica on each machine in your cluster, set
[`mode: global`](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#mode) under the `deploy` section. It's
useful for cluster-wide services like monitoring agents, log collectors, or reverse proxies.
You can also combine `mode: global` with `x-machines` to run one container on each machine in a specific set.
```yaml
services:
monitoring:
image: quay.io/prometheus/node-exporter:latest
deploy:
# Run one container on each machine in the cluster
mode: global
# Optionally run one container on each of these specific machines only
#x-machines:
# - machine-1
# - machine-2
```
## Use a different Compose file location
If your Compose file has a different name or location, use the `-f/--file` flag to specify its path:
```shell
uc deploy -f path/to/your-compose.yaml
```
You can also specify multiple Compose files to merge configurations:
```shell
uc deploy -f compose.yaml -f compose.prod.yaml
```
See [Use multiple Compose files](https://docs.docker.com/compose/how-tos/multiple-compose-files/) for details on how you
can customise your Compose application for different environments or workflows.
## Verify your deployment
After deploying your app, you can verify that your services are running as expected by listing all deployed services in
the cluster:
```shell
uc ls
```
and inspecting the status of containers for a specific service:
```shell
uc inspect web
```
@@ -0,0 +1,3 @@
# Image tag format
TBD