Problem Statement:
Doing a 'sudo docker compose -f compose/docker-compose.yaml up'
on a new VM for a NextJs application docker container, results in:
> [... builder 4/6] RUN npm ci:
1.721 npm error code EUSAGE
1.722 npm error
1.722 npm error The `npm ci` command can only install with an existing package-lock.json or
1.722 npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
1.722 npm error later to generate a package-lock.json file, then try again.
1.722 npm error
1.722 npm error Clean install a project
1.722 npm error
1.722 npm error Usage:
1.722 npm error npm ci
1.722 npm error
1.722 npm error Options:
1.722 npm error [--install-strategy <hoisted|nested|shallow|linked>] [--legacy-bundling]
1.722 npm error [--global-style] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
1.722 npm error [--include <prod|dev|optional|peer> [--include <prod|dev|optional|peer> ...]]
1.722 npm error [--strict-peer-deps] [--foreground-scripts] [--ignore-scripts] [--no-audit]
1.722 npm error [--no-bin-links] [--no-fund] [--dry-run]
1.722 npm error [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
1.722 npm error [-ws|--workspaces] [--include-workspace-root] [--install-links]
1.722 npm error
1.722 npm error aliases: clean-install, ic, install-clean, isntall-clean
1.722 npm error
1.722 npm error Run "npm help ci" for more info
Preface: I've seen similar posts relating to Github actions, but haven't seen any pertaining to Docker that have solved my problem.
I'm trying to deploy a next js application (running in a docker container) to an AWS VM. The steps I took leading to this error are:
1. Locally on my PC, do a 'docker compose -f compose/docker-compose.yaml up'
. The files within the compose folder look like this:
//.dockerignore:
.env
Dockerfile.prod
.dockerignore
.next
.git
.gitignore
node_modules
npm-debug.log
README.md
//DockerFile.prod:
# Development Stage
FROM node:20.18.1-alpine AS development
WORKDIR /app
COPY package*.json package-lock.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# Builder Stage
FROM node:20.18.1-alpine AS builder
WORKDIR /app
COPY package*.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production Stage
FROM node:20.18.1-alpine AS production
WORKDIR /app
# Copy the built artifacts from the builder stage
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
# Set the environment variables (if needed)
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.js"]
//docker-compose.yaml:
services:
web-app:
build:
context: ../
dockerfile: compose/DockerFile.prod
target: production
args:
NEXT_PUBLIC_CLIENTVAR: "clientvar"
ports:
- 3000:3000
environment:
- NODE_ENV=production
depends_on:
- db
restart: unless-stopped
db:
image: postgres:14.15
container_name: postgres
ports:
- 5433:5432
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres-data:
2. Tag the web application and push it to Dockerhub container repository.
3. Install docker on the AWS VM + login
4. Pull the web application container from dockerhub
5. Create the /compose folder and docker files on the VM.
6. Run 'sudo docker compose -f compose/docker-compose.yaml up'
The `npm ci` command can only install with an existing package-lock.json or npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or later to generate a package-lock.json file, then try again.
There are two directives in the Compose file that indicate which image to run. You can build:
an image from local sources, or you can run a prebuilt image:
from elsewhere. Your Compose file has a build:
directive, so it always builds from local sources; it does not use the image you've built and pushed.
The first easy change is to replace the entire build:
block with an image:
reference.
version: '3.8'
services:
web-app:
image: myname/webapp:${WEBAPP_TAG:-latest}
ports:
- 3000:3000
depends_on:
- db
restart: unless-stopped
db: { ... } # same as above
Here I've allowed providing the tag as an environment variable, so that your CI system can produce uniquely-named builds and you can upgrade (or downgrade) by specifying a tag name from an environment variable.
In your local setup where you do in fact want to build from source, you need a second Compose file. Compose supports multiple input files and merges their contents. If your current Compose file is named docker-compose.yml
then the tool will automatically find docker-compose.override.yml
; if you are using a newer version of Compose with just compose.yml
then it will find compose.override.yml
.
The override file can contain only the build:
declaration. All of the other settings will be taken from the main Compose file. This includes image:
, which here gives a name to the image that's built.
# docker-compose.override.yml
version: 3.8
services:
web-app:
build:
context: ../
dockerfile: compose/DockerFile.prod
target: production
args:
NEXT_PUBLIC_CLIENTVAR: "clientvar"
Then on your local system you can build and push an image
export WEBAPP_TAG=$(date +%Y%m%d) # `git rev-parse HEAD` is also a good choice
docker-compose build
docker-compose push
and on the remote system run it (make sure the override file is not present there)
WEBAPP_TAG=20250122 docker-compose up -d