Skaffold doesn’t actually build artifacts in the order you might think, it builds them based on dependencies defined in your skaffold.yaml.
Let’s see it in action. Imagine you have two Docker images: a frontend and a backend. The frontend depends on the backend being available for its integration tests.
Here’s a simplified skaffold.yaml:
apiVersion: skaffold/v2beta29
kind: Config
build:
artifacts:
- image: my-backend
docker:
dockerfile: backend/Dockerfile
- image: my-frontend
docker:
dockerfile: frontend/Dockerfile
deploy:
kubectl:
manifests:
- k8s/*
profiles:
- name: with-tests
build:
artifacts:
- image: my-frontend
# This tells Skaffold that my-frontend depends on my-backend
dependencies:
images:
- my-backend
When you run skaffold dev -p with-tests, Skaffold doesn’t just build my-backend then my-frontend. It first analyzes the dependencies section. Because my-frontend has dependencies.images: [my-backend], Skaffold knows it must build my-backend before it can build my-frontend.
Let’s trace this:
- Skaffold starts. It reads your
skaffold.yaml. - It identifies build artifacts:
my-backendandmy-frontend. - It checks for dependencies. It sees
my-frontendlistsmy-backendas a dependency. - Dependency resolution: Skaffold determines the build order. It sees
my-backendhas no upstream dependencies, andmy-frontenddepends onmy-backend. The resolved order ismy-backendthenmy-frontend. - Build
my-backend. Skaffold executes the build steps formy-backend. - Build
my-frontend. Only aftermy-backendhas successfully built, Skaffold proceeds to buildmy-frontend.
This dependency mechanism is crucial for complex applications where one service’s build or test phase relies on another. For example, you might have a shared library that multiple microservices depend on. You can define that dependency in skaffold.yaml to ensure the library is built and pushed before any service that uses it.
The dependencies field can also point to other Skaffold artifacts defined within the same skaffold.yaml, dependencies.paths for file-based dependencies, or even external Skaffold configurations. This allows for very intricate build graphs.
The most surprising true thing about Skaffold’s artifact dependencies is that they are evaluated before any build actually begins. Skaffold constructs a directed acyclic graph (DAG) of your build artifacts based on these declarations. If there’s a cycle in your dependencies (e.g., A depends on B, and B depends on A), Skaffold will error out before attempting any builds, preventing an infinite loop. This pre-analysis ensures that the build order is always valid and deterministic.
Consider this scenario: you have a base-image that both your backend and frontend images use. You can declare this:
apiVersion: skaffold/v2beta29
kind: Config
build:
artifacts:
- image: base-image
docker:
dockerfile: base/Dockerfile
- image: my-backend
docker:
dockerfile: backend/Dockerfile
dependencies:
images:
- base-image
- image: my-frontend
docker:
dockerfile: frontend/Dockerfile
dependencies:
images:
- base-image
deploy:
kubectl:
manifests:
- k8s/*
When you run skaffold dev, Skaffold will first build base-image. Then, it will build my-backend and my-frontend concurrently (or in parallel if you have multiple builders configured) because they both depend on base-image and have no dependencies on each other. This parallelization is a key optimization Skaffold performs once the dependency graph is resolved.
The next concept you’ll likely encounter is managing complex build graphs with external Skaffold configurations or artifact tagging strategies.