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 siteadmin.example.com
→ The admin panelportal.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.