Skip to content

Migrating from Makefile

If you’ve been using makefile-for-monorepos, this guide explains the differences and how to migrate to EMB.

EMB was created as the spiritual successor to makefile-for-monorepos, addressing its limitations while keeping the same philosophy:

  • Auto-discovery of components (folders with Dockerfiles)
  • Incremental builds using sentinel files
  • Simple commands for common operations
Aspectmakefile-for-monoreposEMB
Configurationconfig.mk + per-component makefile.mkSingle .emb.yml + optional Embfile.yml per component
SyntaxMake syntax (error-prone, hard to debug)YAML (readable, validated)
DependenciesManual declaration in makefilesDeclared in YAML, automatically resolved
FlavorsEnvironment variables + conditionalsFirst-class support with JSON Patch
TasksCustom make targetsDeclarative task definitions with executors
Error messagesCryptic make errorsClear, actionable error messages
ExtensibilityInclude makefilesPlugin system

Here’s how makefile commands map to EMB:

MakefileEMBNotes
make imagesemb resources buildBuild all images
make {component}.imageemb resources build {component}:imageBuild specific image
make images FORCE=1emb resources build --forceForce rebuild
make -j8 imagesemb resources build -j8Build up to 8 resources in parallel (dependency-ordered)
make -j imagesemb resources build -j autoParallel build, auto = min(CPU count, 4)
make -k imagesemb resources build --keep-goingKeep building after a failure, then report all failures

EMB builds serially by default. Unlike make, where parallelism is a property of your invocation, EMB defaults to one resource at a time — so a make -j8 habit silently becomes a serial build unless you pass -j. To get your parallelism back, either pass -j on each invocation, or set a project-wide default in .emb.yml:

defaults:
build:
concurrency: auto # or a positive integer, e.g. 8

The --jobs flag overrides defaults.build.concurrency. Dependency order is always respected: a resource only starts once all of its dependsOn resources have succeeded, so raising -j can never build something out of order.

Like make -k, --keep-going carries on with resources that don’t depend on the failed one and skips the ones that do, reporting every failure at the end. Without it EMB fails fast: the first failure stops new builds from starting (those already running are allowed to finish).

MakefileEMBNotes
make upemb upBuild and start services
make downemb downStop services
make psemb psShow running services
make {component}.logsemb logs {service}View logs (followed by default)
make {component}.bashemb shell {service}Get a shell
make {component}.onemb up {component}Start specific service
make {component}.offemb down {component}Stop specific service
MakefileEMBNotes
make testsemb run testRun all tests (if task defined)
make {component}.testsemb run {component}:testRun component tests

Replace your config.mk with a .emb.yml file:

.emb.yml
project:
name: my-project
plugins:
- autodocker # Auto-discovers components with Dockerfiles
env:
DOCKER_TAG: ${env:DOCKER_TAG:-latest}

If you have per-component makefile.mk files with custom settings, create Embfile.yml files instead:

Before (api/makefile.mk):

DOCKER_BUILD_TARGET = production
DEPENDS_ON = base

After (api/Embfile.yml):

resources:
image:
type: docker/image
dependsOn:
- base:image
params:
target: production

Dependencies between components are now declared in YAML:

Before (config.mk):

api.image: base.image
web.image: base.image

After (in each component’s Embfile.yml):

api/Embfile.yml
resources:
image:
type: docker/image
dependsOn:
- base:image

Custom make targets become EMB tasks:

Before (api/makefile.mk):

api.test:
docker-compose run --rm api npm test
api.lint:
docker-compose run --rm api npm run lint

After (api/Embfile.yml):

tasks:
test:
description: Run tests
executor:
type: docker/cli
run: npm test
lint:
description: Run linter
executor:
type: docker/cli
run: npm run lint

Environment-specific configurations become flavors:

Before (environment variables):

Terminal window
DOCKER_BUILD_TARGET=production make images

After (.emb.yml):

flavors:
production:
patches:
- op: replace
path: /components/api/resources/image/params/target
value: production

Then use:

Terminal window
emb up --flavor production

Both tools use sentinel files for incremental builds. EMB stores them in .emb/sentinels/ instead of .build/.

To clean sentinels:

Terminal window
# Makefile
make clean
# EMB
emb clean

Both tools use docker-compose under the hood. Your existing docker-compose.yml files work with EMB without modification.

One of the biggest improvements is error handling. Instead of cryptic make errors like:

make: *** No rule to make target 'foo.image', needed by 'bar.image'. Stop.

EMB provides clear messages:

Error: Component 'foo' not found
Did you mean: api, web, base?

You can migrate gradually by keeping both systems during transition:

  1. Start with .emb.yml and basic auto-discovery
  2. Migrate one component at a time to Embfile.yml
  3. Update your CI/CD scripts to use emb commands
  4. Remove makefiles once fully migrated

If you encounter issues migrating, open an issue with your makefile configuration and we’ll help you convert it.