How to Split a Django Project into Microservices with One Admin, One Database, and Common Settings

Learn how to split a Django project into microservices while keeping a single admin panel, database, and shared settings. Ideal for scalable projects.

How to Split a Django Project into Microservices with One Admin, One Database, and Common Settings

If you're working on a Django project and need to split it into multiple services while keeping a single admin panel, one database, and shared settings, this guide is for you.

We'll break a Django monolith into three services:

  • example.com → The public site
  • admin.example.com → The admin panel
  • portal.example.com → A portal for registered users

All services will share the same database and settings, and each will run independently.


1. Project Structure

We’ll start by structuring the Django project properly.

myproject/
│── common/
   ├── __init__.py
   ├── settings.py
   ├── urls.py
   ├── wsgi.py
   ├── asgi.py
│── public/
   ├── __init__.py
   ├── views.py
   ├── urls.py
│── admin_panel/
   ├── __init__.py
   ├── views.py
   ├── urls.py
│── portal/
   ├── __init__.py
   ├── views.py
   ├── urls.py
│── manage.py

Each service will have its own URL configuration but share the same settings and database.


2. Setting Up the Common Configuration

All three services will share a common settings.py file inside common/.

common/settings.py

import os
from pathlib import Path

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

SECRET_KEY = 'your-secret-key'
DEBUG = True

ALLOWED_HOSTS = ['example.com', 'admin.example.com', 'portal.example.com']

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'public',
    'admin_panel',
    'portal',
]

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 = 'common.urls'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

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 = 'common.wsgi.application'
ASGI_APPLICATION = 'common.asgi.application'

STATIC_URL = '/static/'
MEDIA_URL = '/media/'

STATICFILES_DIRS = [BASE_DIR / "static"]
MEDIA_ROOT = BASE_DIR / "media"

3. Configuring the Public Site (example.com)

The public-facing site will be responsible for displaying content to visitors.

public/views.py

from django.shortcuts import render

def home(request):
    return render(request, 'public/home.html')

public/urls.py

from django.urls import path
from .views import home

urlpatterns = [
    path('', home, name='home'),
]

4. Setting Up the Admin Panel (admin.example.com)

Django provides a built-in admin panel, and we will serve it under admin.example.com.

admin_panel/urls.py

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('', admin.site.urls),
]

5. Creating the User Portal (portal.example.com)

The user portal will handle registered users and private functionality.

portal/views.py

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, 'portal/dashboard.html')

portal/urls.py

from django.urls import path
from .views import dashboard

urlpatterns = [
    path('dashboard/', dashboard, name='dashboard'),
]

6. Merging URLs into a Single Dispatcher (common/urls.py)

Since we have three separate services, we need a URL dispatcher to route requests based on the subdomain.

common/urls.py

from django.urls import include, path
from django.http import HttpResponse

def default_view(request):
    return HttpResponse("Invalid subdomain", status=404)

urlpatterns = [
    path('', include('public.urls')),  # example.com
    path('admin/', include('admin_panel.urls')),  # admin.example.com
    path('portal/', include('portal.urls')),  # portal.example.com
]

handler404 = default_view

7. Configuring Nginx for Multi-Domain Support

To properly route traffic, we use Nginx as a reverse proxy.

/etc/nginx/sites-available/example

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80;
    server_name admin.example.com;

    location / {
        proxy_pass http://127.0.0.1:8002;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80;
    server_name portal.example.com;

    location / {
        proxy_pass http://127.0.0.1:8003;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Enable the configuration and restart Nginx:

sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/
sudo systemctl restart nginx

8. Running the Services

We will start each service on a different port.

# Run the public site
python manage.py runserver 8001 --settings=common.settings

# Run the admin panel
python manage.py runserver 8002 --settings=common.settings

# Run the portal
python manage.py runserver 8003 --settings=common.settings

Conclusion

With this setup:

  • example.com serves the public site.
  • admin.example.com serves the Django admin.
  • portal.example.com serves the user portal.
  • All services share a common settings.py file and database.
  • Nginx acts as a reverse proxy to route traffic.

This approach keeps things modular, making it easier to scale parts of your application independently while keeping management centralized.

More posts in Python