# Redeploy

**Package:** Laravel Compose | **Version:** 1 | **URL:** https://mozex.dev/docs/laravel-compose/v1/deploying/redeploy

---

```bash
php artisan compose:redeploy            # every stack
php artisan compose:redeploy meilisearch
php artisan compose:redeploy --dry-run  # print the plan, touch nothing
```

The command exits 0 when every stack was redeployed or skipped and 1 when any stack failed, so a deploy script stops there. Compose output streams through, and each stack ends with one line: redeployed, skipped, or failed.

## The steps, in order

The order isn't cosmetic. Each step is placed where it is because of a rollout that went wrong the other way.

**1. Write the env file.** Compose reads it at `up` time. Writing it first is what makes a changed value apply. A compose file that doesn't parse, or a value that can't be written (a line break, an array), fails this stack with the reason in the output, and the next stack still gets its turn.

**2. Refresh the operator link.** Only when a [link directory](https://mozex.dev/docs/laravel-compose/v1/deploying/hosting-platforms) is configured and the daemon is local. If the link can't be refreshed, the exception is reported through the app's exception handler and a line is printed in the deploy output, and the redeploy carries on. The link is convenience; the container is not.

**3. Build.** Only for stacks whose `build()` returns true: `compose build --pull`. A failed build fails the stack, since there's nothing to start.

**4. Pull, result ignored.** `compose pull --ignore-buildable --quiet`. `up` never refreshes a tag it already has locally, so without this a floating tag would freeze at whatever the first deploy pulled. Ignoring the result means a registry outage redeploys the local image instead of failing the rollout. Pulling before the sweep means the old container keeps serving through the download.

**5. Sweep, result ignored.** `docker rm -f` on every `container_name` in the compose file, with `${VAR}` and `${COMPOSE_PROJECT_NAME}` resolved the way compose resolves them. A fixed-name container created under a different project context (the directory was renamed, the stack moved, an older layout ran) is invisible to this project's `up`, which then dies with a name conflict. Removing by name clears it whatever created it. A missing container is the normal case, hence the ignored result.

Container names are global on a Docker host. Two apps on one server that both name a container `meilisearch` would sweep each other's, so prefix names with the app (`shop-meilisearch`); the scaffolder does that for you. [Compose Files](https://mozex.dev/docs/laravel-compose/v1/stacks/compose-files) has the details.

**6. Up.** `compose up --detach --remove-orphans`, plus `--build` for building stacks and `--wait --wait-timeout N` when `wait()` returns a number. `--remove-orphans` clears services removed from the compose file since the last rollout. A failed `up` fails the stack.

Because the sweep runs every time, `up` always creates fresh containers from the env file just written. A value can't be left un-applied on a container that was already running. And the compose process runs with the file's keys unset from its own environment, so the app's `.env` (which Laravel exports to every child process) can't shadow the file either; [Environment Files](https://mozex.dev/docs/laravel-compose/v1/stacks/environment-files) has the details.

A step that runs past its timeout counts as a failed step, nothing more: a stalled pull is ignored like a failed one, a hung `up` fails the stack and fires the failure event, and the next stack still gets its turn. Timeouts live in [configuration](https://mozex.dev/docs/laravel-compose/v1/configuration) and on the stack.

## Waiting for health

`wait()` makes `up` block until every service is running, or healthy where a healthcheck exists, and fail after the given seconds. Turn it on for stacks that later deploy steps talk to: a search index that gets its settings synced right after, for example. A dead just-recreated container is deploy breakage, and this is where it surfaces.

Leave it off for stacks that take minutes to become healthy and nothing in the deploy needs immediately.

## Where it goes in a deploy

Run it after the new release is built and its config is in place, and before anything that talks to a container. With Composer-script style deploys:

```json
"deploy:after": [
    "php artisan compose:redeploy",
    "php artisan search:sync-settings",
    "php artisan horizon:terminate"
]
```

[Hosting Platforms](https://mozex.dev/docs/laravel-compose/v1/deploying/hosting-platforms) has the equivalent for Ploi, Forge, Envoyer, and plain SSH.

## Dry run

```
meili ............................ /srv/app/current/app/Docker/Meilisearch
  env file: /srv/app/current/app/Docker/Meilisearch/.env with MEILISEARCH_KEY, MEILISEARCH_PORT
  $ docker compose --project-name meili --project-directory ... pull --ignore-buildable --quiet
  $ docker rm -f meilisearch
  $ docker compose --project-name meili --project-directory ... up --detach --remove-orphans --wait --wait-timeout 60
```

Env keys are printed, values never are. A stack whose compose file can't be read, or whose `environment()` throws, shows `would fail:` and the reason, and the rest of the plan still prints.

## From code

```php
use Mozex\Compose\Facades\Compose;
use Mozex\Compose\Enums\RedeployResult;

$results = Compose::redeploy();            // ['meili' => RedeployResult::Redeployed, ...]
$results = Compose::redeploy('meili', fn (string $type, string $buffer) => print $buffer);
```

Each stack also fires [events](https://mozex.dev/docs/laravel-compose/v1/operating/events) as it goes.

---

## Table of Contents

- [Introduction](https://mozex.dev/docs/laravel-compose/v1)
- [AI Integration](https://mozex.dev/docs/laravel-compose/v1/ai-integration)
- [Support Us](https://mozex.dev/docs/laravel-compose/v1/support-us)
- [Requirements](https://mozex.dev/docs/laravel-compose/v1/requirements)
- [Changelog](https://mozex.dev/docs/laravel-compose/v1/changelog)
- [Contributing](https://mozex.dev/docs/laravel-compose/v1/contributing)
- [Questions & Issues](https://mozex.dev/docs/laravel-compose/v1/questions-and-issues)
- [About Mozex](https://mozex.dev/docs/laravel-compose/v1/about)
- [Installation](https://mozex.dev/docs/laravel-compose/v1/installation)
- [Configuration](https://mozex.dev/docs/laravel-compose/v1/configuration)

### Stacks

- [Defining a Stack](https://mozex.dev/docs/laravel-compose/v1/stacks/defining-a-stack)
- [Discovery](https://mozex.dev/docs/laravel-compose/v1/stacks/discovery)
- [Environment Files](https://mozex.dev/docs/laravel-compose/v1/stacks/environment-files)
- [Compose Files](https://mozex.dev/docs/laravel-compose/v1/stacks/compose-files)

### Deploying

- [Redeploy](https://mozex.dev/docs/laravel-compose/v1/deploying/redeploy)
- [Hosting Platforms](https://mozex.dev/docs/laravel-compose/v1/deploying/hosting-platforms)
- [Remote Daemons](https://mozex.dev/docs/laravel-compose/v1/deploying/remote-daemons)
- [Doctor](https://mozex.dev/docs/laravel-compose/v1/deploying/doctor)

### Operating

- [Status, Logs, and Down](https://mozex.dev/docs/laravel-compose/v1/operating/status-logs-down)
- [Health Check](https://mozex.dev/docs/laravel-compose/v1/operating/health-check)
- [Events](https://mozex.dev/docs/laravel-compose/v1/operating/events)
- [Testing](https://mozex.dev/docs/laravel-compose/v1/operating/testing)

### Recipes

- [Meilisearch](https://mozex.dev/docs/laravel-compose/v1/recipes/meilisearch)
- [Telegram Bot API](https://mozex.dev/docs/laravel-compose/v1/recipes/telegram-bot-api)