Integrating Dockerfile and Entrypoint Files
In containerized application development, the Dockerfile describes how an image is built, while the ENTRYPOINT instruction determines which process starts when the container launches. For simple services, placing a single command in ENTRYPOINT is enough. However, real-world deployments often require more work before the main process can run.
That work may include checking environment variables, verifying secrets, creating directories, setting permissions, running migrations, or preparing caches. If these steps live directly in the Dockerfile, the image definition becomes harder to read and harder to maintain.
- Readability: Complex shell logic makes the Dockerfile noisy.
- Debuggability: Troubleshooting becomes more difficult because logic is mixed with build instructions.
- Reuse: Teams may duplicate startup scripts across multiple images.
- Signal handling: Shell layers may interfere with PID 1 signal forwarding.
For basic cases, a direct ENTRYPOINT may be acceptable. For production workloads, separating concerns is usually better.
Move Startup Logic Into an Entrypoint Script
The recommended approach is to keep startup logic in a dedicated script and reference it from the Dockerfile.
The entrypoint script can perform checks and preparation, then hand control to the real process.
#!/bin/sh
set -e
if [ -z "$DB_PASSWORD" ]; then
echo "Error: DB_PASSWORD is not set"
exit 1
fi
chmod +x /app/init.sh
exec "$@"
The exec "$@" instruction replaces the current shell process with the target command. This allows the main application to run as PID 1, which helps it receive termination signals correctly.
The Dockerfile then copies the script and sets it as the entrypoint.
FROM node:18-alpine
WORKDIR /app
COPY . .
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
The CMD instruction provides default arguments. When users run the container, any arguments after the image name are passed to the entrypoint.
Key Technical Details
Use exec for the Main Process
Avoid leaving the shell as the final process. Use exec "$@" so the selected process becomes PID 1 and receives signals such as SIGTERM.
Keep the Script Portable
Use #!/bin/sh when the base image may not include Bash. This improves compatibility with lightweight images.
Pass Arguments Correctly
The entrypoint receives arguments from CMD or from docker run. In the script, "$@" holds those arguments.
For instance, docker run myimage nginx -g 'daemon off;' can pass the nginx option through the entrypoint.
Manage Permissions Carefully
Ensure the copied script is executable. Use RUN chmod +x or, with BuildKit, COPY --chmod=755 entrypoint.sh /entrypoint.sh.
Conclusion
Keeping startup logic out of the Dockerfile and into an entrypoint script improves clarity and reliability. It allows teams to handle preparation, validation, and initialization in one place. When paired with exec, proper argument handling, and signal management, this pattern supports stable container lifecycles.