Deployments

The deploy pipeline

Every step of a Nautvia deploy in order, what runs where, and why the store stays up while it happens.

A deploy builds a new release beside the running one and switches to it with a single symlink move. Magento never enters maintenance mode, and a failure at any step leaves the live store exactly as it was.

Before anything starts

A deploy is refused if:

  • another deploy is already running for this environment;
  • a database, a cache or a search cluster is missing;
  • the team has no active subscription.

Provisioning

On the first deploy — and after a teardown — the infrastructure is built before any code is touched:

  1. A team SSH keypair and a private network are created if this is the team's first environment.
  2. Each server is created: application, MySQL, Valkey, OpenSearch.
  3. Each is polled until its installation finishes. This is the slow part; allow up to twenty minutes.
  4. The media volume is created and mounted.
  5. The logical database and its user are created.

On later deploys all of this is already done and skipped.

Build

Runs on the application server, as the deploy user, in a fresh release directory:

curl -L "$TARBALL_URL" | tar -xz --strip-components=1

COMPOSER_MEMORY_LIMIT=-1 composer install \
    --no-dev --no-interaction --no-progress --optimize-autoloader

COMPOSER_AUTH comes from your MAGENTO_COMPOSER_AUTH variable, if you set one — one JSON object can carry several hosts, e.g. repo.magento.com and your Hyvä packagist.

Hyvä themes are built here too. Any theme under app/design/frontend with web/tailwind/package.json gets npm ci && npm run build, so web/css/styles.css does not have to be committed. Node.js 22 is installed on the server the first time a Hyvä theme needs it.

There is no separate build machine. Build and release both run on the server that serves your store — which is why a heavy composer install can make the live site feel slower for a minute, and why sizing the application server matters.

Release

  1. Link shared paths — app/etc/env.php, pub/media and var are symlinked out of the release into shared storage, so media and logs survive the switch.
  2. Install or upgrade. On a brand-new store, setup:install runs with your database, cache and OpenSearch details. On every deploy after that, setup:upgrade --keep-generated.
  3. setup:di:compile.
  4. setup:static-content:deploy for every locale the store uses — its storefronts' locales and its admins' interface locales, plus en_US. Set MAGENTO_LOCALES (e.g. et_EE en_US) to choose them yourself.
  5. Your deploy commands, in order. A failure here aborts the deploy before the switch — see Deploy hooks and commands.
  6. The switch. ln -sfn the new release to current-new, then mv -T it over current. A rename on the same filesystem is atomic: a request either sees the old release or the new one, never a half-updated tree.
  7. Reload PHP-FPM, then bin/magento cache:flush.
  8. Rewrite the crontab so bin/magento cron:run runs every minute against the new release.
  9. Prune old releases, keeping the newest three.
  10. Warm up with a request to the store.

Magento commands run with a 2 GB PHP memory limit.

Only three releases are kept

The prune in step 9 is what bounds disk use, and it also bounds how far back you can roll: a release older than the last three is gone from the server. See Rollbacks.

First install: what is set for you

The very first deploy of a store runs setup:install, which drops every table in the database and flushes the cache first. On an empty database that is housekeeping. It is also why importing your data comes after the first successful deploy, never before — see Migrating a store.

Some values are fixed at install and are not configurable through the dashboard:

Setting Value
Language en_US
Currency EUR
Timezone Europe/Kyiv
URL rewrites on
Search engine OpenSearch
Admin path randomised, not /admin

Change any of these afterwards in the Magento admin or with bin/magento config:set. Static content is compiled for every locale the store is configured with — add a store view in de_DE and the next deploy builds it.

Magento_TwoFactorAuth is disabled during the install, because stock Magento 2.4 cannot complete a login without working outbound mail. Configure mail and re-enable it.

Varnish full page cache (optional)

Magento's officially recommended full page cache serves cached pages from memory in ~1–5 ms without touching PHP. Turn it on per environment under Settings → Varnish full page cache — like a PHP version change, it applies on the next deploy.

What the deploy does when the switch is on:

  • installs Varnish 7 on the app server and points every HTTPS domain at it (nginx TLS → Varnish → nginx → php-fpm);
  • configures Magento (full_page_cache/caching_application = 2) and loads Magento's own generated VCL, regenerated on every release;
  • wires cache purging, so bin/magento cache:flush — which every deploy runs — also clears Varnish.

The cache gets 20% of the machine's RAM. Turning the switch off and deploying puts the environment back on the built-in Redis page cache. Logged-in customers, carts and checkout are never cached — Magento marks those pages private.

If a deploy fails

The store keeps serving the previous release. The failure reason appears at the top of the deployment page and the log is kept. Everyone on the team gets an email and an in-app notification — see Notifications.

Cancelling

Cancel stops the pipeline advancing and marks the deploy cancelled. The script already running on the server is not killed — it finishes harmlessly against a release directory that never becomes current.