docs: new pre-deploy hooks page and x-pre_deploy extension in the reference page

This commit is contained in:
Pasha Sviderski
2026-04-09 20:24:19 +10:00
parent b3b33a82ba
commit f2f6fee89a
6 changed files with 249 additions and 3 deletions
@@ -78,7 +78,7 @@ See [Push local images to cluster machines](1-deploy-app.md#push-local-images-to
## See also ## See also
- [Deploy an app](1-deploy-app.md): Build and deploy from source code or prebuilt images - [Deploy an app](1-deploy-app.md): Build and deploy from source code or pre-built images
- [Deploy a global service](3-deploy-global-services.md): Deploy one service replica on each cluster machine - [Deploy a global service](3-deploy-global-services.md): Deploy one service replica on each cluster machine
- [Compose support matrix](../../8-compose-file-reference/1-support-matrix.md): Supported Compose features and Uncloud - [Compose support matrix](../../8-compose-file-reference/1-support-matrix.md): Supported Compose features and Uncloud
extensions extensions
@@ -63,7 +63,7 @@ The default mode is `replicated`, where you specify the number of replicas.
## See also ## See also
- [Deploy an app](1-deploy-app.md): Deploy from source code or prebuilt images - [Deploy an app](1-deploy-app.md): Deploy from source code or pre-built images
- [Deploy to specific machines](2-deploy-specific-machines.md): Deploy services to specific machines in your cluster - [Deploy to specific machines](2-deploy-specific-machines.md): Deploy services to specific machines in your cluster
- [Compose Specification: deploy.mode](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#mode): - [Compose Specification: deploy.mode](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#mode):
Compose specification for deployment modes Compose specification for deployment modes
@@ -168,6 +168,7 @@ configuration hasn't changed and only redeploy the remaining ones.
## See also ## See also
- [Pre-deploy hooks](5-pre-deploy-hooks.md): Run a command before deploying service containers
- [Deploy an app](1-deploy-app.md): Build and deploy from source code or pre-built images - [Deploy an app](1-deploy-app.md): Build and deploy from source code or pre-built images
- [Compose support matrix](../../8-compose-file-reference/1-support-matrix.md): Supported Compose features and Uncloud - [Compose support matrix](../../8-compose-file-reference/1-support-matrix.md): Supported Compose features and Uncloud
extensions extensions
@@ -0,0 +1,209 @@
# Pre-deploy hooks
Run a one-off command before deploying a service.
Pre-deploy hooks are useful for **one-off tasks** such as:
- Database schema and data migrations
- Uploading static assets to a CDN
- Cache invalidation
- Any setup task that needs to run **once** before new code goes live, not on every container startup
## How it works
When you run `uc deploy` for a service with a pre-deploy hook configured, the hook command runs after building and
pushing the new image (if [building from source](1-deploy-app.md#deploy-from-source-code)) but **before** rolling out
any new containers.
`uc deploy` runs your hook command inside a new container and waits for it to finish or time out (**5 minutes** by
default). This container **inherits** most of the **service's configuration**, including the image, environment
variables, volumes, placement, and compute resources.
If the command exits with code 0, the deployment continues with a normal [rolling update](4-rolling-deployments.md). If
the command fails or times out, the deployment stops immediately with an error. `uc deploy` will display the latest logs
from the hook container to help you diagnose the issue.
The hook runs on one of the machines where the service will be deployed. Similar to service containers, hook containers
can reach other services over the network, connect to databases, and read or write shared volumes. Note that file system
changes do not persist after the hook finishes, except for changes written to shared volumes.
## Usage
Add the [`x-pre_deploy`](../../8-compose-file-reference/1-support-matrix.md#x-pre_deploy) extension to a service in your
Compose file. The only required attribute is `command`, which can be a string or a list of strings, just like the
service's [`command`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#command).
```yaml title="compose.yaml"
services:
web:
build: .
x-pre_deploy:
command: python manage.py migrate
```
:::info note
The `command` replaces the image's default command ([`CMD`](https://docs.docker.com/reference/dockerfile/#cmd)) but the
[`ENTRYPOINT`](https://docs.docker.com/reference/dockerfile/#entrypoint) still runs. If your image has an entrypoint,
the hook command is passed as arguments to it. You can override the entrypoint for the service using
[`entrypoint`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#entrypoint) which applies to both
hook and regular service containers.
:::
Since your command runs in the same image as the service, any tools or dependencies it needs must be installed in that
image.
See [`x-pre_deploy`](../../8-compose-file-reference/1-support-matrix.md#x-pre_deploy) for all available attributes and
their defaults.
### Database migrations
The most common use case for pre-deploy hooks is running database migrations before deploying a new app version:
```yaml title="compose.yaml"
services:
web:
build: .
environment:
DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/postgres
x-ports:
- app.example.com:8000/https
x-pre_deploy:
# Apply Django migrations from the built image before deploying new app containers
command: python manage.py migrate
depends_on:
- db
db:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql
volumes:
db-data:
```
When you run `uc deploy`, the migration runs first inside a new container created with the same image and environment
variables as the `web` service. Only after it succeeds, the deployment starts replacing service containers with the new
image.
### Running multiple commands
If you need to run several tasks before deployment, you can wrap them in a single shell command:
```yaml title="compose.yaml"
services:
web:
build: .
x-pre_deploy:
# Apply Django migrations, collect static files, and upload them to S3 bucket
command: sh -c "python manage.py migrate && python manage.py collectstatic --no-input"
```
For more complex scenarios, create a dedicated script and use it as the hook command:
<Tabs>
<TabItem value="compose.yaml">
```yaml
services:
web:
build: .
x-pre_deploy:
command: ./scripts/pre_deploy.sh
```
</TabItem>
<TabItem value="scripts/pre_deploy.sh">
```bash
#!/bin/bash
# Ensure the script exits immediately with a non-zero code if any command fails.
set -e
python manage.py migrate
python manage.py collectstatic --no-input
python manage.py clear_cache
```
</TabItem>
</Tabs>
Make sure the script is included in your service image and exits with a non-zero code on any command failure (`set -e`).
### Custom environment and user
The hook container inherits environment variables from the service. You can add hook-specific variables or override
existing ones with `environment`. Use `user` to run the command as a different user.
For example, if your service runs as a non-root user but the hook needs root to fix file permissions on a shared volume:
```yaml title="compose.yaml"
services:
web:
build: .
user: app
volumes:
- data:/data
x-pre_deploy:
command: chown -R app:app /data/uploads
user: root
volumes:
data:
```
:::tip
Uncloud automatically sets `UNCLOUD_HOOK_PRE_DEPLOY=true` in the hook container. You can check this variable in a shared
entrypoint script or your command to detect when it's running as a pre-deploy hook versus a regular service container.
:::
## Failure handling
### Non-zero exit code
When the hook command exits with a non-zero code, the deployment stops immediately. No service containers are created or
replaced. `uc deploy` prints the latest logs from the hook container to help you diagnose the issue.
The failed hook container is not automatically removed, so you can inspect it with `uc inspect` and `uc ps`, and fetch
its full logs as part of the service logs:
```shell
uc logs web
```
Fix the issue and run `uc deploy` again to retry.
### Timeout
If the hook doesn't finish within the timeout (default **5 minutes**), Uncloud kills the container and fails the
deployment. The stopped container is kept for inspection, same as with a non-zero exit code.
You can increase the timeout for long-running tasks like large database migrations or data uploads:
```yaml title="compose.yaml"
services:
web:
x-pre_deploy:
command: python manage.py migrate
timeout: 30m
```
### Idempotency
Design your hook commands to be **idempotent** when possible. If a deployment fails after the hook succeeds (for
example, a new container crashes on startup) and you retry with `uc deploy`, the hook runs again.
Most database migration tools handle this naturally since they track which migrations have already been applied.
## See also
- [`x-pre_deploy` reference](../../8-compose-file-reference/1-support-matrix.md#x-pre_deploy): All available attributes
and their defaults
- [Rolling deployments](4-rolling-deployments.md): How Uncloud updates containers with zero downtime
- [Deploy an app](1-deploy-app.md): Build and deploy from source code or pre-built images
@@ -77,6 +77,7 @@ If you rely on a specific Compose feature that is not supported by Uncloud, plea
| `x-caddy` | ✅ Uncloud-specific | Custom Caddy configuration | | `x-caddy` | ✅ Uncloud-specific | Custom Caddy configuration |
| `x-machines` | ✅ Uncloud-specific | Machine placement constraints | | `x-machines` | ✅ Uncloud-specific | Machine placement constraints |
| `x-ports` | ✅ Uncloud-specific | Service port publishing | | `x-ports` | ✅ Uncloud-specific | Service port publishing |
| `x-pre_deploy` | ✅ Uncloud-specific | Pre-deploy hook command |
[volume-drivers]: https://docs.docker.com/engine/storage/volumes/#use-a-volume-driver [volume-drivers]: https://docs.docker.com/engine/storage/volumes/#use-a-volume-driver
@@ -167,3 +168,38 @@ services:
# Short syntax for a single machine # Short syntax for a single machine
# x-machines: machine-1 # x-machines: machine-1
``` ```
### `x-pre_deploy`
Configure a pre-deploy hook to run a one-off command in a separate container and wait for it to finish successfully
before rolling out service containers. It's useful for preparation tasks that need to run before every service
deployment, such as database migrations, static asset uploads, or cache invalidation.
The hook container uses the service's image and inherits its environment variables, volumes, placement, and compute
resources. If the command fails or times out (5 minutes by default), the deployment stops.
```yaml
services:
web:
build: .
x-pre_deploy:
command: python manage.py migrate
environment:
LOG_LEVEL: DEBUG
timeout: 10m
```
#### Attributes
| Attribute | Type | Default | Description |
|---------------|-------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `command` | string / list | (required) | The command to run in the hook container (same format as the service's [`command`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#command)) |
| `environment` | map / list of KEY=VALUE | - | Additional env vars that override or extend the service's [`environment`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#environment) |
| `privileged` | bool | service's value | Override the service's [`privileged`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#privileged) mode |
| `timeout` | duration | `5m` | Max time to wait for the command to finish before killing it (e.g., `1m30s`, `30m`, `1h`) |
| `user` | string | service's value | Override the service's [`user`](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#user) to run as (`user`, `UID`, `user:group`, or `UID:GID`) |
The hook container also gets `UNCLOUD_HOOK_PRE_DEPLOY=true` environment variable set automatically.
See [Pre-deploy hooks](../4-guides/1-deployments/5-pre-deploy-hooks.md) for more details, usage examples, and failure
handling.
@@ -143,7 +143,7 @@ image: myapp:{{gitsha 7}}.${GITHUB_RUN_ID:-local} # GITHUB_RUN_ID not set →
## See also ## See also
- [Deploy an app](../4-guides/1-deployments/1-deploy-app.md): Deploy from source code or prebuilt images - [Deploy an app](../4-guides/1-deployments/1-deploy-app.md): Deploy from source code or pre-built images
- [Compose Build Specification](https://github.com/compose-spec/compose-spec/blob/main/build.md) - [Compose Build Specification](https://github.com/compose-spec/compose-spec/blob/main/build.md)
- [Compose Specification: image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) - [Compose Specification: image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image)
- [Go template documentation](https://pkg.go.dev/text/template) - [Go template documentation](https://pkg.go.dev/text/template)