I'm constantly in awe and amazed by Docker's capabilities (specifically docker-compose).
After you install Docker engine, you simply run:
```docker
docker-compose up -d
```
And you can have a full stack application running, with a consistent runtime and development dependencies, regardless of what operating system or environment you work in.
When you use or develop software across different platforms and devices (at one point I had [[05-22_Mindless Consumption|8 different devices]] across Windows / Mac), Docker is a godsend.
## Development Patterns
One of the patterns I follow now is to have a base Dockerfile that installs all the drivers and dependencies that I need
`Dockerfile`
```docker
FROM python:3.12-slim
RUN ...
WORKDIR /app
```
Build the container image
```bash
docker build -t base .
```
And then use that image in downstream applications.
If you need to extend the base image with additional packages:
```docker
FROM base
COPY requirements.txt
RUN uv pip install -r requirements.txt
COPY . .
```
> [!TIP]
> To prevent Docker from caching frequently changing files, place the last line of code at the bottom of the Dockerfile.
>
> What `COPY . . ` does is that it copies all files and folders on your host system into the container filesystem, and your files and folders are likely to keep changing as you develop your project.
Otherwise you can simply use the base image without specifying a build context in docker-compose.yml
```docker
services:
<service_name>:
image: base
```