You can run multiple copies of a container off of a single image. A Dockerfile RUN
command only runs once when the image is built; the entrypoint script runs every time you start the container.
As a general rule, I might suggest doing as much as you can in the Dockerfile. There are some parts of the system that aren't available until runtime, like environment-variable settings and other containers; you can't access these until the container starts, which means the entrypoint script.
So, for example:
- You should
RUN composer ...
and other commands that install software or libraries in the Dockerfile; these don't depend on the runtime environment and you don't want to repeat them every time the container runs.
- If you have database migrations, you should
COPY
the migration files into the image, but the database isn't available at image-build time, so the actual migrations need to be run in the entrypoint script.
- If you need to interact with the contents of volumes, those aren't available until the container starts, and this needs to be done in the entrypoint script. (But it's better to avoid needing this; prefer a database in another container to using mounted files for storage.)
I'm a little less familiar with PHP, but a typical Python example might look like:
# Dockerfile
FROM python:3.11
WORKDIR /app
# Install this application's library dependencies
# (Only once, during the image build)
COPY requirements.txt ./
RUN pip install -r requirements.txt
# Copy in the rest of the application code
# (Including the entrypoint script and database migrations)
COPY ./ ./
# Metadata to run the application
EXPOSE 8000
ENTRYPOINT ["./entrypoint.sh"]
CMD ["./manage.py", "runserver", "0.0.0.0:8000"]
#!/bin/sh
# entrypoint.sh
# Run migrations at application startup. The database isn't
# available until now, and its contents are independent of
# this image. Gets repeated every time the container starts.
./manage.py migrate
# Switch to the main container `CMD`.
exec "$@"
# docker-compose.yml
version: '3.8'
services:
app:
build: ./
ports: ['8000:8000']
environment:
PGHOST: db
other PostgreSQL-related variables: as well
# Note, no volumes:
db:
image: postgres:14
environment:
POSTGRES_PASSWORD: passw0rd
et: cetera
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata: