You're actually injecting your source code using volumes:, not during the image build, and this doesn't honor .dockerignore.

Running a Docker application like this happens in two phases:

  1. You build a reusable image that contains the application runtime, any OS and language-specific library dependencies, and the application code; then
  2. You run a container based on that image.

The .dockerignore file is only considered during the first build phase. In your setup, you don't actually COPY anything in the image beyond the requirements.txt file. Instead, you use volumes: to inject parts of the host system into the container. This happens during the second phase, and ignores .dockerignore.

The approach I'd recommend for this is to skip the volumes:, and instead COPY the required source code in the Dockerfile. You should also generally indicate the default CMD the container will run in the Dockerfile, rather than requiring it it the docker-compose.yml or docker run command.

FROM python:3.9-slim-buster

# Do the OS-level setup _first_ so that it's not repeated
# if Python dependencies change
RUN apt-get update && apt-get install -y ...

WORKDIR /django-app

# Then install Python dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt

# Then copy in the rest of the application
# NOTE: this _does_ honor .dockerignore
COPY . .

# And explain how to run it
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
USER userapp
# consider splitting this into an ENTRYPOINT that waits for the
# the database, runs migrations, and then `exec "$@"` to run the CMD
CMD sleep 7; python manage.py migrate; python manage.py runserver 0.0.0.0:8000

This means, in the docker-compose.yml setup, you don't need volumes:; the application code is already inside the image you built.

version: "3.8"
services:
  app: 
    build: .
    ports: 
      - 8000:8000
    depends_on: 
      - db
    # environment: [PGHOST=db]
    # no volumes: or container_name:

  db:
    image: postgres
    volumes: # do keep for persistent database data
      - ./data:/var/lib/postgresql/data
    environment: 
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    # ports: ['5433:5432']

This approach also means you need to docker-compose build a new image when your application changes. This is normal in Docker.

For day-to-day development, a useful approach here can be to run all of the non-application dependencies in Docker, but the application itself outside a container.

# Start the database but not the application
docker-compose up -d db

# Create a virtual environment and set it up
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt

# Set environment variables to point at the Docker database
export PGHOST=localhost PGPORT=5433

# Run the application locally
./manage.py runserver

Doing this requires making the database visible from outside Docker (via ports:), and making the database location configurable (probably via environment variables, set in Compose with environment:).

Answer from David Maze on Stack Overflow
🌐
GitHub
gist.github.com › KernelA › 04b4d7691f28e264f72e76cfd724d448
.dockerignore example for Python projects · GitHub
.dockerignore example for Python projects. GitHub Gist: instantly share code, notes, and snippets.
🌐
GitHub
github.com › themattrix › python-pypi-template › blob › master › .dockerignore
python-pypi-template/.dockerignore at master · themattrix/python-pypi-template
Template for quickly creating a new Python project and publishing it to PyPI. - python-pypi-template/.dockerignore at master · themattrix/python-pypi-template
Author   themattrix
Top answer
1 of 2
21

You're actually injecting your source code using volumes:, not during the image build, and this doesn't honor .dockerignore.

Running a Docker application like this happens in two phases:

  1. You build a reusable image that contains the application runtime, any OS and language-specific library dependencies, and the application code; then
  2. You run a container based on that image.

The .dockerignore file is only considered during the first build phase. In your setup, you don't actually COPY anything in the image beyond the requirements.txt file. Instead, you use volumes: to inject parts of the host system into the container. This happens during the second phase, and ignores .dockerignore.

The approach I'd recommend for this is to skip the volumes:, and instead COPY the required source code in the Dockerfile. You should also generally indicate the default CMD the container will run in the Dockerfile, rather than requiring it it the docker-compose.yml or docker run command.

FROM python:3.9-slim-buster

# Do the OS-level setup _first_ so that it's not repeated
# if Python dependencies change
RUN apt-get update && apt-get install -y ...

WORKDIR /django-app

# Then install Python dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt

# Then copy in the rest of the application
# NOTE: this _does_ honor .dockerignore
COPY . .

# And explain how to run it
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
USER userapp
# consider splitting this into an ENTRYPOINT that waits for the
# the database, runs migrations, and then `exec "$@"` to run the CMD
CMD sleep 7; python manage.py migrate; python manage.py runserver 0.0.0.0:8000

This means, in the docker-compose.yml setup, you don't need volumes:; the application code is already inside the image you built.

version: "3.8"
services:
  app: 
    build: .
    ports: 
      - 8000:8000
    depends_on: 
      - db
    # environment: [PGHOST=db]
    # no volumes: or container_name:

  db:
    image: postgres
    volumes: # do keep for persistent database data
      - ./data:/var/lib/postgresql/data
    environment: 
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    # ports: ['5433:5432']

This approach also means you need to docker-compose build a new image when your application changes. This is normal in Docker.

For day-to-day development, a useful approach here can be to run all of the non-application dependencies in Docker, but the application itself outside a container.

# Start the database but not the application
docker-compose up -d db

# Create a virtual environment and set it up
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt

# Set environment variables to point at the Docker database
export PGHOST=localhost PGPORT=5433

# Run the application locally
./manage.py runserver

Doing this requires making the database visible from outside Docker (via ports:), and making the database location configurable (probably via environment variables, set in Compose with environment:).

2 of 2
14

That's not actually your case, but in general an additional cause of ".dockerignore not ignoring" is that it applies the filters to whole paths relative to the context dir, not just basenames, so the pattern:

__pycache__
*.pyc

applies only to the docker context's root directory, not to any of subdirectories.

In order to make it recursive, change it to:

**/__pycache__
**/*.pyc
🌐
Docker
docs.docker.com › guides › python › containerize your app
Containerize a Python application
You can use the Python Docker Official Image, or a Docker Hardened Image (DHI). Docker Hardened Images (DHIs) are minimal, secure, and production-ready base images maintained by Docker. They help reduce vulnerabilities and simplify compliance. For more details, see Docker Hardened Images. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started!
🌐
GitHub
github.com › GoogleCloudPlatform › getting-started-python › blob › main › optional-kubernetes-engine › .dockerignore
getting-started-python/optional-kubernetes-engine/.dockerignore at main · GoogleCloudPlatform/getting-started-python
Code samples for using Python on Google Cloud Platform - getting-started-python/optional-kubernetes-engine/.dockerignore at main · GoogleCloudPlatform/getting-started-python
Author   GoogleCloudPlatform
🌐
TestDriven.io
testdriven.io › tips › 6850ab62-9323-4dca-8ddf-8db1d479accc
Tips and Tricks - Use a .dockerignore File | TestDriven.io
A properly structured .dockerignore file can help: Decrease the size of the Docker image · Speed up the build process · Prevent unnecessary cache invalidation · Prevent leaking secrets · Example: **/.git **/.gitignore **/.vscode **/coverage **/.env **/.aws **/.ssh Dockerfile README.md docker-compose.yml **/.DS_Store **/venv **/env ·
🌐
Codefinity
codefinity.com › courses › v2 › 1b3ab5c8-4816-472a-800a-5504df606754 › b881f527-09f4-4a90-8233-d62037eff3ff › c4cc10a0-4848-463f-9432-bbd883990211
Learn Dockerignore File | Docker for Flask
A Dockerignore file is a configuration file that tells Docker which files and folders should not be included in the Docker image build context. It allows you to specify which files or folders to ignore during the image creation process, helping ...
Find elsewhere
Top answer
1 of 1
22

Yes, it's a recommended practice. There are several reasons:

Reduce the size of the resulting image

In .dockerignore you specify files that won't go to the resulting image, it may be crucial when you're building the smallest image. Roughly speaking the size of bytecode files is equal to the size of actual files. Bytecode files aren't intended for distribution, that's why we usually put them into .gitignore as well.


Cache related problems

In earlier versions of Python 3.x there were several cached related issues:

Python’s scheme for caching bytecode in .pyc files did not work well in environments with multiple Python interpreters. If one interpreter encountered a cached file created by another interpreter, it would recompile the source and overwrite the cached file, thus losing the benefits of caching.

Since Python 3.2 all the cached files prefixed with interpreter version as mymodule.cpython-32.pyc and presented under __pychache__ directory. By the way, starting from Python 3.8 you can even control a directory where the cache will be stored. It may be useful when you're restricting write access to the directory but still want to get benefits of cache usage.

Usually, the cache system works perfectly, but someday something may go wrong. It worth to note that the cached .pyc (lives in the same directory) file will be used instead of the .py file if the .py the file is missing. In practice, it's not a common occurrence, but if some stuff keeps up being "there", thinking about remove cache files is a good point. It may be important when you're experimenting with the cache system in Python or executing scripts in different environments.


Security reasons

Most likely that you don't even need to think about it, but cache files can contain some sort of sensitive information. Due to the current implementation, in .pyc files presented an absolute path to the actual files. There are situations when you don't want to share such information.


It seems that interacting with bytecode files is a quite frequent necessity, for example, django-extensions have appropriate options compile_pyc and clean_pyc.

🌐
PyPI
pypi.org › project › dockerignore-generate
dockerignore-generate
August 14, 2018 - JavaScript is disabled in your browser. Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
TestDriven.io
testdriven.io › blog › docker-best-practices
Docker Best Practices for Python Developers | TestDriven.io
February 12, 2024 - You can also help prevent unwanted cache invalidations by using a .dockerignore file to exclude unnecessary files from being added to the Docker build context and the final image.
🌐
Reddit
reddit.com › r/docker › is .dockerignore treated differently using docker compose?
r/docker on Reddit: Is .dockerignore treated differently using docker compose?
March 24, 2024 -

Previously, using a single Dockerfile with it's associated .dockerignore, it worked fine. However, when trying to follow this through with docker compose, it now fails to work. I will provide my structure.

I have uploaded my folder structure to https://imgur.com/65bjhat for easy reading.

My Dockerfile is,

# Use the official Python image as a base image
FROM python:3.9

Prevents Python from writing pyc files to disc (equivalent to python -B option)
ENV PYTHONDONTWRITEBYTECODE 1

Prevents Python from buffering stdout and stderr (equivalent to python -u option)
ENV PYTHONUNBUFFERED 1

WORKDIR /app

COPY . /app/

RUN pip install -r requirements.txt

My .dockerignore is,

venv/*
**/migrations
.dockerignore
Dockerfile
images/*

My docker-compose.yaml is

services:
  web:
    build: ./app
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./app:/usr/src/app/
    ports:
      - "8000:8000"
    env_file:
      - ./.dev.env
    depends_on:
      - db
  db:
    image: postgres:16
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=admin
      - POSTGRES_DB=radix_fitness_postgresql_db

volumes:
  postgres_data:

I run this using,

docker compose -f compose-dev.yaml up

However, when looking inside my container, everything inside my .dockerignore is still prevent such as my migration folders, venv, etc...

The only thing I did was switch to docker compose, but it seems to be treated identically as my Dockerfile inside of the app directory should be using the .dockerignore. I wonder if COPY is overriding the behaviour of .dockerignore?

🌐
Medium
medium.com › analytics-vidhya › containerizing-python-flask-application-19faa9db031c
Containerizing python flask application | by Augustas Berneckas | Analytics Vidhya | Medium
April 30, 2021 - Let’s create .dockerignore file and ignore unnecessary files: .idea/ .vscode/ .git/ .venv/ __pycache__/ .gitignore Dockerfile · docker build -t python-demo-app:init .
🌐
DEV Community
dev.to › francescoxx › dockerize-a-django-application-4fa
Dockerize a Django Application - DEV Community
April 12, 2023 - RUN pip install -r requirements.txt This will install the python dependencies INSIDE the Docker Image · COPY . . This command will copy all the file and folders (included the Dockerfile) in the Image filesystem. al files and folders included in the .dockerignore file will be ignored
🌐
Sourcegraph
sourcegraph.com › github.com › python-gitlab › python-gitlab › - › blob › .dockerignore
.dockerignore - python-gitlab/python-gitlab - Sourcegraph
January 21, 2022 - Search across 2 million+ open source repositories for free. Powered by Sourcegraph Code Search.
🌐
GitHub
github.com › topics › dockerignore
dockerignore · GitHub Topics · GitHub
api docker django-rest-framework generic dockerignore ... A Python module for filtering a list of files according to patterns.
🌐
PyPI
pypi.org › project › generate-dockerignore-from-gitignore
generate-dockerignore-from-gitignore · PyPI
usage: generate-dockerignore [-h] [-C DOCKER_ROOT] [-v] [-o OUTPUT] [PATH ...] .dockerignore file generator based on .gitignore file(s) positional arguments: PATH gitignore file(s), or directories under git source control (which will be searched for **/.gitignore).
      » pip install generate-dockerignore-from-gitignore
    
Published   Aug 14, 2023
Version   0.1.3
🌐
.dockerignore
dockerignore.com › dockerignores › languages-python
Python .dockerignore
Ready-to-use .dockerignore template for Python languages projects. Copy and paste directly into your project.
🌐
GeeksforGeeks
geeksforgeeks.org › how-to-use-a-dockerignore-file
How to Use a .dockerignore File? - GeeksforGeeks
April 8, 2024 - Now, similar to a .gitignore file that is commonly used when you build Git repositories, a .dockerignore file is used to ignore files and folders when you try to build a Docker Image.