Fix Sequelize "Cannot find /config/config.json" in Docker
Reproduced in Docker: why moving WORKDIR alone still fails, why build-time migrations cannot reach the database, and a Compose setup that migrates on start.
TL;DR#
sequelize db:migrate resolves config/config.json relative to the directory the command runs in, not relative to the project or to node_modules. In a Dockerfile, a RUN step that comes before any WORKDIR runs in /, so the CLI looks for /config/config.json and stops. Moving WORKDIR up is not enough when COPY . . still comes after the migration step: the message just changes to /app/config/config.json. And once the config is found, a RUN migration still cannot reach a database that only exists as a Compose service. The fix that works end to end is to set WORKDIR, copy the project, and run the migration when the container starts, after the database is healthy.
The error comes from a Stack Overflow question (8 votes, about 27,000 views). Its Dockerfile installs sequelize-cli globally, runs sequelize db:migrate and sequelize db:seed:all, and only afterwards sets WORKDIR /ng-app and runs COPY . .. A Dockerfile with the same instruction order, built on 10 October 2026 with node:22-alpine, sequelize-cli 6.6.5 and Sequelize 6.37.8, fails like this:
#8 ERROR: process "/bin/sh -c npx sequelize-cli db:migrate" did not complete successfully: exit code: 1
2.101 Sequelize CLI [Node: 22.23.3, CLI: 6.6.5, ORM: 6.37.8]
2.129 ERROR: Cannot find "/config/config.json". Have you run "sequelize init"?Why the CLI looks in /config#
The message is printed by getMigrator() in src/core/migrator.js, and only when two things are both false: the config file exists, and a connection URL was passed on the command line. The path it prints is built in src/helpers/config-helper.js:
const api = {
// ...other members of the helper omitted
getConfigFile() {
if (args.config) {
return path.resolve(process.cwd(), args.config);
}
const defaultPath = path.resolve(process.cwd(), 'config', 'config.json');
const alternativePath = defaultPath.replace('.json', '.js');
return helpers.path.existsSync(alternativePath)
? alternativePath
: defaultPath;
},
};Everything hangs on the current working directory of the process. The CLI does not search upwards, and it does not look next to package.json or node_modules. So the absolute path in the error tells you exactly where the CLI was standing when it ran: /config/config.json means the working directory was /.
In a Dockerfile that working directory comes from WORKDIR. The Dockerfile reference says that it "sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it", and that "If not specified, the default working directory is /", while noting that a base image may already set one. With the official node:22-alpine image, a RUN placed above the first WORKDIR ran in / in the reproduction, which is why the path came out as /config/config.json.
The .sequelizerc file follows the same rule. The CLI loads it from the current directory as well (src/core/yargs.js resolves .sequelizerc against the working directory), so a .sequelizerc in the project root is ignored when the command runs from somewhere else.
Fixing the Dockerfile and the Compose file#
The question's accepted answer suggests moving WORKDIR /ng-app above the Sequelize commands. That is the right first move, but it is not the whole fix. Three things have to be true at the moment the migration runs: the working directory is the project, the project files are there, and the database answers.
-
Set
WORKDIRbefore anything that reads project files, and copy the project before the CLI runs. WithWORKDIRmoved up butCOPY . .still after the migration (the accepted answer applied on its own), the build still fails, now one directory deeper:text3.919 Sequelize CLI [Node: 22.23.3, CLI: 6.6.5, ORM: 6.37.8] 3.956 ERROR: Cannot find "/app/config/config.json". Have you run "sequelize init"?In the question's Dockerfile the same thing holds: the config folder only arrives with the
COPY . .near the end, so the CLI would report the path under/ng-appinstead. -
Do not run the migration as a
RUNstep. WithWORKDIRandCOPY . .both above it, the CLI now finds the file, then fails to reach the database: a build step does not run on the Compose network where the service namedbresolves (reproduced here with a plaindocker build):text2.104 Sequelize CLI [Node: 22.23.3, CLI: 6.6.5, ORM: 6.37.8] 2.129 Loaded configuration file "config/config.json". 6.583 ERROR: getaddrinfo ENOTFOUND dbSo the same
RUN sequelize db:migrateline had two independent problems: where it ran, and when it ran. -
Run the migration when the container starts. This is the Dockerfile that was built and run in the reproduction:
dockerfileFROM node:22-alpine WORKDIR /app COPY package.json ./ RUN npm install --no-audit --no-fund COPY . . CMD ["sh", "-c", "npx sequelize-cli db:migrate && node index.js"]With
npm installand no production flag, packages listed underdevDependenciesare installed, so the locally installed CLI is there. If your image installs only production dependencies, listsequelize-cliunderdependenciesinstead, or the start command has no CLI to run. Installing it locally also avoids the question's global install. -
Make the API wait for a healthy database. Starting the migration before PostgreSQL accepts connections only moves the failure. Compose's
depends_onwithcondition: service_healthywaits for the dependency's healthcheck (see the Compose services reference):yamlservices: db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: postgres POSTGRES_DB: app healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d app"] interval: 2s timeout: 3s retries: 20 api: build: context: ./app dockerfile: fixed.dockerfile depends_on: db: condition: service_healthyThe
config/config.jsonused with it points at the Compose service name, not atlocalhost:json{ "development": { "username": "postgres", "password": "postgres", "database": "app", "host": "db", "dialect": "postgres" } }
The question's Dockerfile also runs sequelize db:seed:all at build time. It reads the same config and needs the same database, so it belongs in the start command as well, after the migration.
Confirm the migration ran#
Start the stack with docker compose up -d --build and read the API logs with docker compose logs api. In the reproduction the first start printed:
Sequelize CLI [Node: 22.23.3, CLI: 6.6.5, ORM: 6.37.8]
Loaded configuration file "config/config.json".
Using environment "development".
== 20261012000000-create-notes: migrating =======
== 20261012000000-create-notes: migrated (0.034s)
app startedThe CLI records applied migrations in the SequelizeMeta table, which you can query from the database container:
name
--------------------------------
20261012000000-create-notes.js
(1 row)Running migrations on every start is safe because of that table. After docker compose restart api, the second start skipped the work:
Loaded configuration file "config/config.json".
Using environment "development".
No migrations were executed, database schema was already up to date.
app startedWhen the config lives somewhere else, or is not shipped at all#
The same message shows up in setups where the Dockerfile order is already right. The first cases below were run inside the working container from the previous section; the .dockerignore case used a rebuilt image run on its own.
docker exec from another directory. Running the CLI by its full path from / (docker compose exec -w / api /app/node_modules/.bin/sequelize-cli db:migrate:status) brings the original error straight back, even though the file is in the image:
ERROR: Cannot find "/config/config.json". Have you run "sequelize init"?Run it from the project directory, or pass the config path explicitly. From /, adding the config file and migrations folder paths as options worked:
Loaded configuration file "app/config/config.json".
Using environment "development".
up 20261012000000-create-notes.jsConfig in a non-default folder. With the config moved to db/config/config.json, the CLI reports /app/config/config.json as missing. A .sequelizerc in the project root fixes it; the Sequelize migrations guide documents this file and its keys, and this is the one that was tested:
const path = require('path');
module.exports = {
config: path.resolve('db', 'config', 'config.json'),
'migrations-path': path.resolve('db', 'migrations'),
};Loaded configuration file "db/config/config.json".
Using environment "development".
up 20261012000000-create-notes.jsIt only helps when the CLI starts in that directory: the same command run from / failed again with /config/config.json, because the CLI never read the .sequelizerc in /app.
Config excluded from the build context. A config line in .dockerignore (common when the file holds credentials) removes the folder from the image, and the container fails at start with Cannot find "/app/config/config.json". Check what COPY . . actually received; the Vite index.html page shows the same .dockerignore trap with another file. If you keep credentials out of the image on purpose, the migrations guide documents a connection-string option instead of a config file; in the reproduction, a run with a connection URL and no config file at all printed Parsed url postgres://postgres:*****@db:5432/app and listed the migration.
The suggestion in the message. "Have you run sequelize init?" is a hint for a new project, not a fix here. When no config file exists, init writes a default config.json from getDefaultConfig(), which uses the MySQL dialect, user root and host 127.0.0.1. The CLI stops complaining about the file, and the next step fails against a database that is not there.
The next error after the config is found#
Once the CLI reads your config, the failure moves to the connection. If the migration still runs during the build, it is getaddrinfo ENOTFOUND db, as shown above. If the config says "host": "localhost" (as the sequelize init answer on the question does), the CLI inside the container dials its own loopback; continue with ECONNREFUSED 127.0.0.1:5432 in Docker Compose, which explains why localhost is the container itself and how to wait for a healthy database. If the app's own sequelize.authenticate() then waits forever instead of failing, see Sequelize authenticate() hangs with no success or error, which traces it to the pg connection timeout default of no timeout. And if your Dockerfile also compiles TypeScript before migrating, the same production-install question decides whether tsc is there; sh: tsc: not found in Docker build covers it. A Next.js image fails the same way when a statically rendered page fetches another Compose service during docker build; getaddrinfo ENOTFOUND only in the production Next.js image moves that fetch to request time.
The useful habit is to read the absolute path in the message literally. It is the CLI's working directory plus config/config.json, nothing more, so it tells you which directory the command ran in and therefore which Dockerfile line, exec call or start command to change. Reproduced on 10 October 2026 with Docker Engine 29.7.2, node:22-alpine (Node 22.23.3), postgres:16-alpine, sequelize-cli 6.6.5 and Sequelize 6.37.8.
Further reading: Fix: React hot reload doesn't work in a Docker container.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Continue Reading
Fix Cannot find module 'dotenv' in Docker Compose (Node)
Reproduced step by step in Compose: why npm install in the Dockerfile is not enough under a bind mount, and why a new package still goes missing after a rebuild.
Fix Docker: Client Version Is Too Old
Separate an outdated Docker client from a pinned API or the wrong context, and check the minimum your exact Engine release accepts.
Fix Nginx 502 in Docker Compose: upstream localhost
Trace the upstream address in the Nginx log, prove the Node listener is reachable from the proxy, and separate a wrong host from a loopback-only bind.


