Skip to main content

Build Cache

Vessl caches Docker build layers between deploys. On a warm cache, most builds skip the dependency installation step entirely — a Laravel or Node.js app that took 4–5 minutes on the first deploy typically rebuilds in under a minute on subsequent pushes.

How caching works

Whether you use Nixpacks (auto-detected) or a custom Dockerfile, the build runs on Vessl's build server using Docker BuildKit. BuildKit caches each layer keyed by its inputs. If the inputs haven't changed since the last build, the layer is reused from cache.

For Nixpacks, the cache boundary is usually right after the dependency install step:

Layer 1: base image ← almost always cached
Layer 2: system packages ← cached until nixpacks plan changes
Layer 3: install dependencies ← cached until lockfile changes
Layer 4: copy app source ← invalidated on every push
Layer 5: build app ← runs if layer 4 changed

This means: if you only change application code (not package.json, composer.json, requirements.txt, etc.), the dependency install step is skipped entirely.

For Dockerfiles, the same rules apply — layer order matters. Put COPY package.json ./ and RUN npm install before COPY . . to avoid reinstalling dependencies on every code change.

When the cache is bypassed

A few things will invalidate cached layers and force a full rebuild:

  • Changing your package.json, composer.json, requirements.txt, go.mod, Gemfile, or any other dependency manifest
  • Changing your Dockerfile
  • Changing build arguments (via Build Args in app Settings)
  • Manually clicking Deploy without cache (see below)

Force a clean build

If you need to bypass the cache entirely — for example, to pick up a new upstream package version or to debug a build that succeeds in cache but fails fresh — click the dropdown arrow next to the Deploy button and choose Deploy without cache.

This passes --no-cache / --pull to the build, which forces every layer to rebuild from scratch. The next deploy after that will be slow (full dependency install), but subsequent deploys return to the warm-cache path.

First deploy is always slow

The very first deploy of a new app always runs a full build — there's nothing cached yet. This is expected. Subsequent deploys are much faster.

Tips for faster builds

1. Keep your lockfile committed

Nixpacks and most package managers use the lockfile to determine the exact packages to install. A missing or uncommitted lockfile forces the package manager to re-resolve versions on every build, which is slower and can produce inconsistent results.

Always commit package-lock.json, yarn.lock, composer.lock, Pipfile.lock, or go.sum.

2. Order Dockerfile layers from least to most volatile

# Good — dependencies cached separately from app code
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Bad — any code change invalidates the npm ci layer
COPY . .
RUN npm ci && npm run build

3. Use .dockerignore to exclude large irrelevant directories

Copying large directories (like node_modules/ or .git/) into the build context slows down the context transfer step. Add a .dockerignore at your repo root:

node_modules/
.git/
.env*
*.log

Vessl injects a minimal .dockerignore automatically, but your own file takes precedence if present.

4. Don't install dev dependencies in production

# Node.js — skip devDependencies
npm ci --omit=dev

# Composer — skip require-dev
composer install --no-dev --optimize-autoloader

# pip — use a requirements.txt without dev packages
pip install -r requirements.txt

Fewer packages = smaller image = faster build and deploy.

Build cache and disk usage

Build cache accumulates over time on the build server. Vessl automatically prunes old cache weekly to keep disk usage in check. If you notice unusually slow builds despite unchanged dependencies, the cache may have been evicted — the next deploy will rebuild and re-warm it automatically.

To check current disk usage on your server, see the Overview tab on the server page.