Django #

Django is the most mature and stable Python web framework. With its “batteries included” philosophy, Django ships with an ORM, admin panel, authentication system, form handling, and much more. For web applications with complex business logic — CMSs, internal dashboards, e-commerce backends, ERP systems — Django is a very safe choice because almost everything you need is already in the framework.

Docker Compose complements Django elegantly: the application, database, cache, and message broker run in separate containers. No need to install Postgres or Redis on every developer’s laptop. No more “but it works on my machine”. This article covers a Docker Compose setup for Django local development, from Python Dockerfiles, docker-compose with healthchecks, to production-readiness best practices.

Prerequisites #

Make sure you have installed:

  • Docker and Docker Compose (latest versions)
  • Python 3.12+ (optional, for host-side development)
  • Poetry or pip (optional)

A standard Django project structure (single project layout):

my-django-app/
├── manage.py
├── requirements.txt
├── Dockerfile
├── Dockerfile.dev
├── docker-compose.yml
├── .env
├── .dockerignore
├── app/                       # Django project (settings)
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
├── core/                      # Main app
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── urls.py
│   ├── serializers.py         # DRF
│   ├── migrations/
│   └── tests.py
└── db/
    └── init/                  # SQL init scripts
        └── 01-extensions.sql

Or with a more scalable layout (multi-project):

my-django-app/
├── manage.py
├── requirements/
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
├── config/                    # Project config
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── dev.py
│   │   └── prod.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
├── apps/
│   ├── users/
│   ├── products/
│   └── orders/
└── ...

Splitting requirements/base.txt, dev.txt, and prod.txt is a common pattern for mid-size projects. base.txt holds the core dependencies, dev.txt adds development tooling, and prod.txt contains production drivers like Gunicorn.

A Dockerfile for Production #

For production, a multi-stage Dockerfile with a slim base image.

# syntax=docker/dockerfile:1.6
FROM python:3.12-slim AS builder

WORKDIR /app

# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies into a separate prefix
COPY requirements/base.txt requirements.txt
RUN pip install --upgrade pip \
    && pip install --prefix=/install --no-cache-dir -r requirements.txt

# Second stage: a slim runtime
FROM python:3.12-slim

WORKDIR /app

# Runtime OS dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq5 \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Copy installed Python packages from the builder
COPY --from=builder /install /usr/local

# Copy source code
COPY . .

# Non-root user
RUN addgroup -S django && adduser -S django -G django
USER django

EXPOSE 8000

# Gunicorn for production
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]

The final image is based on python:3.12-slim (~150 MB), containing source code + Gunicorn. A non-root user for security.

A Dockerfile for Development #

For development, a single stage with auto-reload.

# Dockerfile.dev
FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1

# OS dependencies for building Python packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements/dev.txt requirements.txt
RUN pip install --upgrade pip \
    && pip install -r requirements.txt

# Source code is mounted via a volume
EXPOSE 8000

# runserver with auto-reload enabled
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

PYTHONDONTWRITEBYTECODE=1 prevents .pyc files (not needed in development, and it reduces host/container inconsistencies). PYTHONUNBUFFERED=1 makes logs appear on stdout without buffering — important for debugging.

docker-compose.yml #

# docker-compose.yml
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.dev
    image: django-app:dev
    container_name: django-web
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./:/app
    ports:
      - "8000:8000"
    environment:
      - DJANGO_SETTINGS_MODULE=config.settings.dev
      - DATABASE_URL=postgres://django:pass@db:5432/djangoapp
      - REDIS_URL=redis://cache:6379/0
      - DEBUG=1
      - SECRET_KEY=local-dev-secret-change-me
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    container_name: django-db
    environment:
      - POSTGRES_USER=django
      - POSTGRES_PASSWORD=dev
      - POSTGRES_DB=djangoapp
    volumes:
      - db-data:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U django -d djangoapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"

  cache:
    image: redis:7-alpine
    container_name: django-cache
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    volumes:
      - cache-data:/data
    ports:
      - "6379:6379"

  # Optional: MailHog for email testing
  mailhog:
    image: mailhog/mailhog:latest
    container_name: django-mailhog
    ports:
      - "1025:1025"
      - "8025:8025"

  # Optional: pgAdmin for a database UI
  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: django-pgadmin
    environment:
      - [email protected]
      - PGADMIN_DEFAULT_PASSWORD=admin
    ports:
      - "5050:80"
    depends_on:
      - db
    profiles: ["tools"]

volumes:
  db-data:
  cache-data:

Service Explanations #

web — the main Django service. Uses Dockerfile.dev with runserver. Source code is bind-mounted — every file save triggers a Django auto-reload. Port 8000 is exposed to the host.

db — PostgreSQL for persistent data. The pg_isready healthcheck keeps web from starting before the database is ready. The db/init folder holds SQL scripts for extensions or schemas loaded once when the container first starts.

cache — Redis for caching, sessions, and the Celery broker. The default mode is enough for development.

mailhog — a mock SMTP server for email testing. Django sends email to MailHog on port 1025, and the web UI at http://localhost:8025 shows the “sent” emails.

pgadmin — optional (enable with docker compose --profile tools up -d). A web UI for Postgres.

Settings Configuration #

Split settings per environment for clean code.

config/settings/base.py:

import os
from pathlib import Path
from urllib.parse import urlparse

BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "insecure-default-key-change-me")
DEBUG = os.environ.get("DJANGO_DEBUG", "0") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django.contrib.postgres",

    # Third-party
    "rest_framework",
    "django_celery_results",

    # Local
    "apps.users",
    "apps.products",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "config.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"

# Database
DATABASE_URL = os.environ.get("DATABASE_URL", "postgres://django:pass@db:5432/djangoapp")
db_params = urlparse(DATABASE_URL)
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": db_params.path.lstrip("/"),
        "USER": db_params.username,
        "PASSWORD": db_params.password,
        "HOST": db_params.hostname,
        "PORT": db_params.port or "5432",
    }
}

# Cache
REDIS_URL = os.environ.get("REDIS_URL", "redis://cache:6379/0")
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": REDIS_URL,
    }
}

# Celery
CELERY_BROKER_URL = REDIS_URL
CELERY_RESULT_BACKEND = "django-db"
CELERY_CACHE_BACKEND = "django-cache"
CELERY_TIMEZONE = "UTC"
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60

# Internationalization
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True

# Static files
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

# Default primary key field type
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Email for development
EMAIL_BACKEND = os.environ.get(
    "EMAIL_BACKEND",
    "django.core.mail.backends.console.EmailBackend",
)
EMAIL_HOST = os.environ.get("EMAIL_HOST", "mailhog")
EMAIL_PORT = int(os.environ.get("EMAIL_PORT", "1025"))

config/settings/dev.py:

from .base import *  # noqa

DEBUG = True

# Detailed error pages
INTERNAL_IPS = ["127.0.0.1", "host.docker.internal"]

# Django Debug Toolbar
if os.environ.get("ENABLE_DEBUG_TOOLBAR", "1") == "1":
    INSTALLED_APPS += ["debug_toolbar"]  # noqa
    MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")  # noqa
    import socket
    hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
    INTERNAL_IPS = [ip[: ip.rfind(".")] + ".1" for ip in ips] + ["127.0.0.1", "10.0.2.2"]

# Console email backend if not using MailHog
# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

# Allow all hosts in dev
ALLOWED_HOSTS = ["*"]

# Print SQL queries in dev
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {"class": "logging.StreamHandler"},
    },
    "loggers": {
        "django.db.backends": {
            "handlers": ["console"],
            "level": "DEBUG",
        },
    },
}

Models, Views, and Serializers #

Model (apps/users/models.py):

import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(unique=True)
    full_name = models.CharField(max_length=255)
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to="avatars/", blank=True, null=True)
    is_verified = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username", "full_name"]

    class Meta:
        db_table = "users"
        indexes = [
            models.Index(fields=["email"]),
            models.Index(fields=["created_at"]),
        ]

    def __str__(self):
        return self.email


class Post(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    content = models.TextField()
    is_published = models.BooleanField(default=False)
    published_at = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "posts"
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["slug"]),
            models.Index(fields=["is_published", "-published_at"]),
        ]

Serializer (apps/users/serializers.py):

from rest_framework import serializers
from .models import User


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "email", "full_name", "bio", "is_verified", "created_at"]
        read_only_fields = ["id", "is_verified", "created_at"]


class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, min_length=8)
    password_confirm = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ["email", "username", "full_name", "password", "password_confirm"]

    def validate(self, data):
        if data["password"] != data["password_confirm"]:
            raise serializers.ValidationError("passwords do not match")
        return data

    def create(self, validated_data):
        validated_data.pop("password_confirm")
        password = validated_data.pop("password")
        user = User(**validated_data)
        user.set_password(password)
        user.save()
        return user

ViewSet (apps/users/views.py):

from rest_framework import viewsets, status, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer, RegisterSerializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    lookup_field = "id"

    def get_permissions(self):
        if self.action == "create":
            return [permissions.AllowAny()]
        return super().get_permissions()

    def create(self, request, *args, **kwargs):
        serializer = RegisterSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        return Response(
            UserSerializer(user).data,
            status=status.HTTP_201_CREATED,
        )

    @action(detail=False, methods=["get"], permission_classes=[permissions.IsAuthenticated])
    def me(self, request):
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)

URLs (apps/users/urls.py):

from rest_framework.routers import DefaultRouter
from .views import UserViewSet

router = DefaultRouter()
router.register(r"users", UserViewSet, basename="user")

urlpatterns = router.urls

Root URL (config/urls.py):

from django.contrib import admin
from django.urls import path, include
from django.http import JsonResponse


def healthz(request):
    return JsonResponse({"status": "ok"})


urlpatterns = [
    path("admin/", admin.site.urls),
    path("healthz", healthz),
    path("api/v1/", include("apps.users.urls")),
]

Database Migrations #

Django has a very solid built-in migration system.

# Create migrations after changing models
docker compose exec web python manage.py makemigrations

# Apply migrations
docker compose exec web python manage.py migrate

# View migration status
docker compose exec web python manage.py showmigrations

# Create a superuser for the admin
docker compose exec web python manage.py createsuperuser

For production environments, migrate runs at container startup via an entrypoint script.

entrypoint.sh:

#!/bin/sh
set -e

echo "Waiting for database..."
while ! nc -z db 5432; do
  sleep 1
done
echo "Database ready."

echo "Applying migrations..."
python manage.py migrate --noinput

echo "Collecting static files..."
python manage.py collectstatic --noinput

echo "Starting server..."
exec "$@"
# Add to Dockerfile.prod
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

Django Debug Toolbar #

The Django Debug Toolbar is a must-have development tool. It shows SQL queries, request/response headers, template context, and much more.

requirements/dev.txt:

-r base.txt
django-debug-toolbar==4.2.0
django-extensions==3.2.3
ipython==8.12.0

Make sure the middleware and URLs are registered. In dev.py:

INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")

import socket
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS = [ip[: ip.rfind(".")] + ".1" for ip in ips] + ["127.0.0.1", "10.0.2.2", "host.docker.internal"]

In config/urls.py:

if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns

Access at http://localhost:8000 — the toolbar appears in the top-right corner. Click to expand and see SQL queries, templates, signals, etc.

Build and Run #

# Build the image
docker compose build

# Run all services
docker compose up -d

# View logs
docker compose logs -f web

# Run migrations
docker compose exec web python manage.py migrate

# Create a superuser
docker compose exec web python manage.py createsuperuser

# Django shell
docker compose exec web python manage.py shell

# Collect static files (for production)
docker compose exec web python manage.py collectstatic

# Stop
docker compose down

# Full reset
docker compose down -v

Access:

  • Web: http://localhost:8000
  • Admin: http://localhost:8000/admin/
  • API: http://localhost:8000/api/v1/
  • PostgreSQL: localhost:5432
  • Redis: localhost:6379
  • MailHog UI: http://localhost:8025
  • pgAdmin (optional): http://localhost:5050 (login: [email protected] / admin)

Test the endpoint:

# Register a user
curl -X POST http://localhost:8000/api/v1/users/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "username": "rina",
    "full_name": "Rina Wati",
    "password": "password123",
    "password_confirm": "password123"
  }'

# List users
curl http://localhost:8000/api/v1/users/

# Get the current user (needs auth)
curl -H "Authorization: Bearer your-token-here" http://localhost:8000/api/v1/users/me/

Testing with Pytest-Django #

Pytest is more ergonomic than Django’s built-in unittest.

requirements/dev.txt:

-r base.txt
pytest==8.0.0
pytest-django==4.7.0
pytest-cov==4.1.0
factory-boy==3.3.0

pytest.ini:

[pytest]
DJANGO_SETTINGS_MODULE = config.settings.test
python_files = test_*.py *_test.py tests.py
addopts = -ra -q --strict-markers
testpaths = apps

apps/users/tests/test_models.py:

import pytest
from apps.users.models import User


@pytest.mark.django_db
def test_create_user():
    user = User.objects.create_user(
        email="[email protected]",
        username="test",
        full_name="Test User",
        password="password123",
    )
    assert user.email == "[email protected]"
    assert user.check_password("password123")
    assert not user.is_verified


@pytest.mark.django_db
def test_user_str():
    user = User.objects.create_user(
        email="[email protected]",
        username="test",
        full_name="Test",
        password="password123",
    )
    assert str(user) == "[email protected]"

apps/users/tests/test_api.py:

import pytest
from rest_framework.test import APIClient


@pytest.fixture
def client():
    return APIClient()


@pytest.mark.django_db
def test_register_user(client):
    response = client.post(
        "/api/v1/users/",
        {
            "email": "[email protected]",
            "username": "test",
            "full_name": "Test",
            "password": "password123",
            "password_confirm": "password123",
        },
        format="json",
    )
    assert response.status_code == 201
    assert response.data["email"] == "[email protected]"
    assert "id" in response.data
docker compose exec web pytest

When Django Fits, and When It Doesn’t #

Use Django if:
  ✓ Web apps with many data models
  ✓ You need an out-of-the-box admin panel
  ✓ Complex authentication systems
  ✓ Traditional apps (server-rendered, form-heavy)
  ✓ A team already familiar with Django

Avoid Django if:
  ✗ You only need a lightweight API microservice (use FastAPI/Flask)
  ✗ Real-time apps with heavy WebSocket usage
  ✗ A pure SPA frontend (Django becomes overkill)
  ✗ A startup needing fast iteration without an admin/ORM

Django is very powerful but also carries many assumptions. For very lightweight API microservices, FastAPI or Flask fit better. For SPAs, Django REST Framework + React/Vue is a common combination — but make sure you actually need the ORM and admin.

Best Practices #

Split Settings per Environment #

Don’t hardcode configuration in one settings.py. Use base.py + dev.py + prod.py + test.py. Override via environment variables.

Use Environment Variables for Secrets #

SECRET_KEY, database passwords, API keys — all from the environment. Provide a .env.example in the repo, but .env goes in .gitignore.

Use a Custom User Model #

From the start of a project, define a custom User model extending AbstractUser. Replacing the user model after an app is running is expensive.

# settings/base.py
AUTH_USER_MODEL = "users.User"

Keep Migrations in Version Control #

Always commit migration files to Git. Don’t generate migrations in production — always develop first, test, then deploy.

Index Frequently-Used Queries #

For fields that are often filtered or sorted, add db_index=True or Meta.indexes. This prevents full table scans.

Healthchecks and Retries #

The database must have a healthcheck. The web service uses depends_on: condition: service_healthy. Add a retry loop or an entrypoint script that waits for the database to be ready.

Bind-Mount Source Code in Dev #

Mount source code into the container. Django’s runserver auto-reloads on every file save. For production, COPY the source into the final image.

Use MailHog for Email Testing #

Don’t send real emails in development. Use MailHog (or MailPit in Docker) to capture emails and view them in the web UI.

Troubleshooting #

Port 8000 Already in Use #

lsof -i :8000
# or
netstat -ano | findstr :8000

Stop the process or change the port mapping.

Migration Failures #

# Reset the database
docker compose down -v
docker compose up -d
docker compose exec web python manage.py migrate

Static Files 404 #

In development, Django serves static files automatically. In production, run collectstatic and serve from Nginx or a CDN.

Hot Reload Not Working #

Make sure the bind mount covers the whole project. Check the logs for Python errors — sometimes an import error prevents the reload.

Database Connection Refused #

Use depends_on: condition: service_healthy. Add an entrypoint script that waits for the database. Or use wait-for-it.sh.

Summary #

  • Django is ideal for web apps with complex business logic — ORM, admin, and auth out of the box.
  • Multi-stage Dockerfiles for production: a builder stage installs dependencies, a slim runtime stage. The image is < 200 MB.
  • Dockerfile.dev for development: full Python + tools. Use runserver for auto-reload.
  • Split settings per environment: base.py + dev.py + prod.py + test.py. Override via environment variables.
  • Use a custom User model from the start. Replacing the model after an app is running is expensive.
  • Healthchecks for dependent services: Postgres pg_isready, Redis redis-cli ping. Use depends_on: condition: service_healthy.
  • MailHog for email testing in development. The UI is at http://localhost:8025.
  • Django Debug Toolbar for SQL queries, request/response inspection, and template debugging.
  • Pytest-Django for testing. @pytest.mark.django_db for database-accessing tests. Factory-boy for fixtures.
  • Migrations in version control — always commit. Generate in dev, apply in prod.
  • Index frequently-used queries for performance. db_index=True or Meta.indexes.
  • Best practices: split settings, environment variables for secrets, a custom User model, healthchecks, dev bind mounts, MailHog for email.
  • Alternatives: Flask for lightweight APIs, FastAPI for modern type-hinted APIs, Pylons/Pyramid for flexibility.
  • Use Django if you need the ORM, admin, and form handling. Avoid it if you only need a lightweight API microservice.

← Previous: Chi   Next: Flask →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact