Baby Tools Shop - Containerized with Docker
A Django-based e-commerce web application for baby products, containerized with Docker for easy deployment and development.
TOC
- Description
- Quickstart
- Running the Container
- Configuration
- Project Structure
- Tech Stack
- Security Notes
- Known Limitations
- License
URL: https://github.com/hpetersen2/baby-tools-shop
Checkout this repository to see the code/implementation
Description
This project demonstrates how to containerize an existing Django application with Docker. The same image runs on a local machine for development and on a V-Server for production, so the application behaves identically in both environments.
Repository contents:
- Django web application (Python 3.9, Django 4.0.2)
- Dockerfile for building the image
entrypoint.sh, which prepares the database, creates a superuser, loads demo data and starts Gunicorn- SQLite3 database configuration
Quickstart
Prerequisites:
- Docker Desktop installed and running
- Git installed on your system
Steps:
-
Clone the repository and change into the app folder (the Dockerfile lives in
babyshop_app):git clone https://github.com/hpetersen2/baby-tools-shop.gitcd baby-tools-shop/babyshop_app -
Make sure
entrypoint.shuses LF line endings (important on Windows). With CRLF the container fails to start. In VS Code, click "CRLF" in the bottom-right corner, select "LF" and save. -
Build the Docker image:
docker build -t babyshop_app -f Dockerfile . -
Run the container:
docker run -it --rm -p 8025:8025 babyshop_app -
Open
http://localhost:8025in your browser.
Running the Container
Development Mode
docker run -it --rm -p <host-port>:8025 <image-name>
-it: interactive terminal, so you see the logs live--rm: removes the container when it stops (Ctrl+C)-p <host-port>:8025: maps a port of your machine to the container port
Example:
docker run -it --rm -p 3000:8025 babyshop_app
The app is then available at http://localhost:3000.
Production Mode (V-Server)
Prerequisites: Docker and SSH access on the V-Server (see V-Server Setup).
-
Connect to the server, clone the repository and build the image there:
ssh <username>@<your-server-ip>git clone https://github.com/hpetersen2/baby-tools-shop.gitcd baby-tools-shop/babyshop_appdocker build -t babyshop_app -f Dockerfile . -
Start the container in the background:
docker run -d --restart=always --name babyshop_app_container \-p 8025:8025 \-e DJANGO_SUPERUSER_PASSWORD='<a-strong-password>' \babyshop_app
-d: runs the container in the background--restart=always: restarts the container after a crash or a server reboot--name: gives the container a fixed name for easy management-e: sets an environment variable (see Environment Variables)
The SQLite database (db.sqlite3) lives inside the container. docker rm deletes it, including all users and orders. To keep it, create the file on the host once and mount it:
touch db.sqlite3
docker run -d --restart=always --name babyshop_app_container \
-p 8025:8025 \
-v "$(pwd)/db.sqlite3:/app/db.sqlite3" \
babyshop_app
Create the file before the first run. Otherwise Docker creates a directory with that name.
Updating the Application
git pull
docker stop babyshop_app_container
docker rm babyshop_app_container
docker build -t babyshop_app -f Dockerfile .
docker run -d --restart=always --name babyshop_app_container -p 8025:8025 babyshop_app
Use the same docker run options (-e, -v) as for the first start. Without the database volume, the update resets all data.
Useful Commands
| Command | Description |
|---|---|
docker ps | Show running containers |
docker ps -a | Show all containers, including stopped ones |
docker logs <container-name> | Show container logs |
docker logs -f <container-name> | Follow logs in real time |
docker stop <container-name> | Stop a running container |
docker start <container-name> | Start a stopped container |
docker restart <container-name> | Restart a container |
docker rm <container-name> | Remove a stopped container |
docker rm -f <container-name> | Force-remove a running container |
Configuration
Port
The container listens on port 8025. This value comes from the environment variable PORT, which is set in the Dockerfile and read by entrypoint.sh. You do not need to edit any file to change it.
Change only the host port (container stays on 8025):
docker run -it --rm -p 3000:8025 babyshop_app
Change the container port as well:
docker run -it --rm -e PORT=9000 -p 9000:9000 babyshop_app
The format of -p is <host-port>:<container-port>. If the host port is already in use (for example by Nginx on port 80), pick another one.
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
PORT | 8025 | Port Gunicorn listens on inside the container |
DJANGO_SUPERUSER_USERNAME | admin | Username of the admin account created on startup |
DJANGO_SUPERUSER_EMAIL | admin@example.com | Email of the admin account |
DJANGO_SUPERUSER_PASSWORD | adminpassword | Password of the admin account |
If you do not set DJANGO_SUPERUSER_PASSWORD, the admin account is created with the publicly known password adminpassword. Always set your own value on a server.
You can pass several variables from a file with --env-file:
docker run -d --restart=always --name babyshop_app_container \
--env-file .env -p 8025:8025 babyshop_app
Keep this .env file out of Git. It is already listed in .dockerignore, so it is not copied into the image.
Container Naming
Use clear names, especially when several containers run on the same host:
docker run -d --restart=always --name babyshop_prod -p 8025:8025 babyshop_app
- Use descriptive names with an environment indicator (
babyshop_dev,babyshop_prod) - Use only lowercase letters, numbers, underscores and hyphens
Django Settings
All Django settings are in babyshop_app/babyshop/settings.py.
Database: SQLite3, stored as db.sqlite3 in the project root.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Debug mode: DEBUG = True shows detailed error pages. Set it to False in production.
Allowed hosts: With DEBUG = False, Django only accepts requests for hosts listed in ALLOWED_HOSTS:
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'yourdomain.com', 'your-server-ip']
After changing settings.py, rebuild the image.
Project Structure
babyshop_app/
├── Dockerfile # Builds the image (python:3.9-alpine)
├── entrypoint.sh # Runs on container start (LF line endings required)
├── requirements.txt # Python dependencies
├── docker-compose.yml # Compose file (not used in this guide)
├─ ─ manage.py
├── babyshop/ # Django project (settings.py, urls.py, wsgi.py)
├── products/ # Products and categories
├── users/ # Login and registration
└── templates/ # HTML templates
On start, entrypoint.sh collects static files, runs migrations, creates the superuser, adds demo categories and products, and starts Gunicorn.
Tech Stack
- Python 3.9
- Django 4.0.2
- Gunicorn (WSGI server)
- SQLite3 (database)
- Docker (containerization)
- Git/GitHub (version control)
Security Notes
- Never commit secrets such as SSH keys, passwords, API tokens or
.envfiles. - Replace the Django
SECRET_KEY. The key insettings.pyis adjango-insecure-...placeholder that is public in the repository. Generate a new one for production and load it from an environment variable. - Set
DEBUG = Falsein production to avoid leaking internal information. - Change the default admin password (see Environment Variables).
- Use HTTPS for anything reachable from the internet, for example with Nginx as a reverse proxy in front of the container.
Known Limitations
This project is a learning exercise and not hardened for production use:
- Outdated versions: Python 3.9 and Django 4.0.2 are end-of-life and have known vulnerabilities. Upgrade them before real use.
- Development flag in production:
entrypoint.shstarts Gunicorn with--reload, which is meant for development. - Root user: The container runs as root. A dedicated unprivileged user would be safer.
- Hardcoded settings:
SECRET_KEYandDEBUGare set directly insettings.pyinstead of being read from environment variables. docker-compose.yml: The Compose file maps port 8000, while the app listens on 8025. It needs adjusting before it can be used.
License
This project is licensed under the MIT License. See the LICENSE file for details.