Dockerizing Mitsuki Applications
Containerizing your Mitsuki application with Docker allows you to create a portable, scalable, and consistent environment for development, testing, and production.
This guide will walk you through creating a Dockerfile for a typical Mitsuki application.
Table of Contents
- Prerequisites
- Basic Project Structure
- Creating a
.dockerignorefile - Creating the
Dockerfile - Building the Docker Image
- Running the Docker Container
- Production-Ready
Dockerfile(Multi-stage) - Configuration in Docker
Prerequisites
Ensure you have Docker installed on your system. You can download it from the official Docker website.
Basic Project Structure
Let's assume your Mitsuki application has the following structure:
my_app/
├── app/
│ ├── __init__.py
│ ├── controllers.py
│ ├── services.py
│ └── main.py
├── application.yml
├── requirements.txt
└── Dockerfileapp/main.py: Your Mitsuki application entry point.requirements.txt: Your Python dependencies.application.yml: Your base configuration file.Dockerfile: The file we will create.
Your requirements.txt should look something like this:
mitsuki
# Other dependencies like asyncpg for PostgreSQL
asyncpgCreating a .dockerignore file
To keep your Docker image small and speed up builds, create a .dockerignore file in your project root. This file tells Docker which files and directories to exclude from the build context.
For example, a .dockerignore could look like this:
# Git
.git
.gitignore
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.venv/
.env
# IDEs
.vscode/
.idea/Creating the Dockerfile
Here is a simple, single-stage Dockerfile for a Mitsuki application:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app/main.py"]Note: When exposing ports on Docker, you'll want to bind your application to the 0.0.0.0 host, not localhost or 127.0.0.1.
If you haven't written many Dockerfiles before - a quick summary of what's being done here:
FROM python:3.11-slim: Starts from a lightweight official Python 3.11 image.WORKDIR /app: Sets the default directory for subsequent commands inside the container.COPY requirements.txt .: Copies only therequirements.txtfile first. Docker's layer caching means thepip installstep will only be re-run if this file changes, not every time your source code changes.RUN pip install --no-cache-dir -r requirements.txt: Installs your Python dependencies.--no-cache-dirkeeps the image size smaller.COPY . .: Copies your application source code and configuration files into the/appdirectory in the container.CMD ["python", "app/main.py"]: Specifies the command to execute when the container starts.
Building the Docker Image
Navigate to your project root (where the Dockerfile is) and run the docker build command:
docker build -t my-mitsuki-app .-t my-mitsuki-app: Tags your image with a memorable name..: Specifies the current directory as the build context.
Running the Docker Container
Once the image is built, you can run it as a container:
docker run -p 8000:8000 --name mitsuki-container my-mitsuki-app-p 8000:8000: Maps port 8000 on your host machine to port 8000 in the container.--name mitsuki-container: Assigns a name to your running container for easy reference.
Your Mitsuki application should now be accessible at http://localhost:8000.
Using Production Profile
It's best practice to run your containerized application with a production profile. You can set the active profile using an environment variable.
docker run -p 8000:8000 \
-e MITSUKI_PROFILE=production \
--name mitsuki-container \
my-mitsuki-app-e MITSUKI_PROFILE=production: Sets theMITSUKI_PROFILEenvironment variable inside the container, which will make Mitsuki load theapplication-production.ymlconfiguration.
Production-Ready Dockerfile (Multi-stage)
For production, you would usually use a multi-stage build to create a smaller, more secure image that doesn't contain build-time dependencies. Compiled languages can also avoid storing their raw source code in the built images this way, but it's irrelevant for Python as it's an interpreted language.
Here is a more robust, multi-stage Dockerfile:
# --- Build Stage ---
# Use a full Python image for building dependencies that might have C extensions
FROM python:3.11 as builder
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# --- Final Stage ---
# Use a slim image for the final, smaller image
FROM python:3.11-slim
# Create a non-root user for security
RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser
# Copy installed packages and application code from the build stage
COPY --from=builder /app /app
# Set the working directory
WORKDIR /app
# Expose the port
EXPOSE 8000
# Run the application
CMD ["python", "app/main.py"]Why is this better for production?
- Smaller Image Size: The final image is based on
python:3.11-slimand doesn't include the build tools and libraries that might have been needed to install dependencies in thebuilderstage. - Improved Security:
- It runs the application as a non-root user (
appuser), which is a critical security best practice. - It contains only the necessary installed packages and code, reducing the attack surface.
- It runs the application as a non-root user (
Configuration in Docker
Your application inside Docker likely needs to connect to other services like databases. Hardcoding secrets like database credentials in application.yml is neither secure nor flexible. The best practice is to provide them at runtime using environment variables.
Mitsuki's configuration system has a clear precedence: YAML files > Environment Variables > Code Defaults. For an environment variable to be used, the key must not be present in the loaded YAML files. For more on configuration, read Configuration.
1. Omit Secrets from application-production.yml
To configure the database URL via an environment variable, remove the url key from your production YAML file. This forces the framework to fall back to checking for an environment variable.
# application-production.yml
database:
# The 'url' key is intentionally omitted.
# The framework will fall back to the MITSUKI_DATABASE_URL environment variable.
pool:
enabled: true
size: 20
server:
# The app must listen on 0.0.0.0 to be accessible from outside the container.
host: 0.0.0.0Your code using @Value will still work as expected:
# e.g., in a @Configuration class
db_url: str = Value("${database.url}") # Will be populated by the env var