Skip to content

Django + Vue Notes App

A full-stack notes application built with Django and Vue.js, running as a single container with PostgreSQL database integration. This example demonstrates how AppMecca handles database provisioning, credential injection, and migration management.

Source: git@gitlab.com:appmecca-sample-apps/django-vue-notes.git

What This App Demonstrates

  • Database integration -- database: enabled: true auto-provisions PostgreSQL credentials
  • Django migrations via entrypoint scripts
  • The APPMECCA_SERVICE env var -- guards migrations so only the web service runs them
  • Database backups and restores via the CLI
  • Trailing-slash health checks -- Django's APPEND_SLASH setting means the health check path is /health/ (not /health)

appmecca.yaml

containers:
  - name: web
    dockerfile: Dockerfile
    context: .

deploy:
  - name: web
    container: web
    port: 8000
    health_check:
      path: /health/

database:
  enabled: true

The only addition compared to a simple web app is the database section. When enabled: true, AppMecca:

  1. Creates a dedicated PostgreSQL database for this app and branch on the managed database server
  2. Generates a secure password and keeps it in the platform secret store
  3. Injects DATABASE_URL, DB_HOST, DB_PORT, DB_NAME, DB_USER, and DB_PASSWORD into every container

The database and user names are generated identifiers (something like am_a1b2c3_db), not django-vue-notes_db — always read them from the environment.

Trailing slash on Django health checks

Django's APPEND_SLASH setting redirects /health to /health/ with a 301. The platform health check follows redirects, but this adds latency. Set the health check path to /health/ (with the trailing slash) to avoid the redirect entirely.

How Migrations Work

AppMecca does not run migrations for you. Instead, your container's entrypoint script handles them on every deployment.

entrypoint.sh

#!/bin/bash
set -e

echo "==> Waiting for database..."

RETRIES=10
DELAY=3

for i in $(seq 1 $RETRIES); do
    python -c "
import psycopg2
import os
try:
    conn = psycopg2.connect(
        host=os.environ.get('DB_HOST', 'localhost'),
        port=os.environ.get('DB_PORT', '5432'),
        dbname=os.environ.get('DB_NAME', 'app'),
        user=os.environ.get('DB_USER', 'app'),
        password=os.environ.get('DB_PASSWORD', ''),
    )
    conn.close()
    print('Database is ready.')
except Exception as e:
    print(f'Database not ready: {e}')
    exit(1)
" && break

    echo "    Attempt $i/$RETRIES failed. Retrying in ${DELAY}s..."
    sleep $DELAY
done

# Only the web service runs migrations
if [ "$APPMECCA_SERVICE" = "web" ]; then
    echo "==> Running migrations..."
    python manage.py migrate --noinput
fi

echo "==> Starting gunicorn..."
exec gunicorn config.wsgi:application \
    --bind 0.0.0.0:8000 \
    --workers 2 \
    --timeout 120 \
    --access-logfile - \
    --error-logfile -

The script does three things:

  1. Waits for the database -- it may take a few seconds to become reachable after your container starts. The retry loop prevents crash-on-first-deploy.
  2. Guards migrations with APPMECCA_SERVICE -- AppMecca sets this variable automatically to the service name from your appmecca.yaml. If you later add a worker service, only the web service will run migrations.
  3. Starts the application with exec -- Replaces the shell process so that Docker signals (SIGTERM) reach gunicorn directly for graceful shutdown.

The APPMECCA_SERVICE variable

AppMecca injects APPMECCA_SERVICE into every container with the service name from the deploy section. For this app, the web container gets APPMECCA_SERVICE=web. This is useful when multiple services share the same image and you need to conditionally run setup steps like migrations.

Django settings

Configure Django to read credentials from the injected environment variables:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": os.environ.get("DB_HOST", "localhost"),
        "PORT": os.environ.get("DB_PORT", "5432"),
        "NAME": os.environ.get("DB_NAME", "app"),
        "USER": os.environ.get("DB_USER", "app"),
        "PASSWORD": os.environ.get("DB_PASSWORD", ""),
    }
}

Or use DATABASE_URL with dj-database-url for a one-liner:

import dj_database_url

DATABASES = {
    "default": dj_database_url.config(default=os.environ.get("DATABASE_URL"))
}

Deploy It

# Register the app
mecca register git@gitlab.com:appmecca-sample-apps/django-vue-notes.git -e dev

# Build the Docker image
mecca build trigger -e dev -g git@gitlab.com:appmecca-sample-apps/django-vue-notes.git

# Deploy
mecca app deploy -e dev django-vue-notes
Expected output
Deploying django-vue-notes to dev...
Deployment queued: deploy-xxxxxxxx
Waiting for agents...
Container started: django-vue-notes-web
Health check passed: /health/ -> 200 OK
Deployment complete.

Services:
  web: https://django-vue-notes-web.dev.appmecca.net (healthy)

Your app is live at https://django-vue-notes-web.dev.appmecca.net. The service name is always part of the hostname, even for a single-service app — run mecca app show django-vue-notes -e dev to see the exact URL for your group.

Managing the Database

Once deployed, use the app-level backup commands to create backups and restore from them. A backup is a bundle — the database plus any declared drives, with a manifest — so a restore brings back a consistent set rather than a lone SQL file.

Create a backup

mecca app backup django-vue-notes -d "initial data" -e dev
Expected output
Backup queued for django-vue-notes...
Waiting for completion...
Backup complete: backups/dev/django-vue-notes/main/manual/2026-06-15T10-23-45Z/ (2.3 MB)

List backups

mecca app backups django-vue-notes -e dev
Expected output
Backups for django-vue-notes (dev):

BUNDLE PREFIX                                              SIZE     DESCRIPTION    CREATED
backups/dev/django-vue-notes/main/manual/2026-06-15T10-23-45Z/   2.3 MB   initial data   2026-06-15 10:23:45
backups/dev/django-vue-notes/main/manual/2026-06-14T08-00-00Z/   1.8 MB   pre-release    2026-06-14 08:00:00

Restore from a backup

mecca app restore django-vue-notes \
  backups/dev/django-vue-notes/main/manual/2026-06-15T10-23-45Z/ --force -e dev

The second argument is a bundle prefix (a directory), not a path to a .sql.gz file. Copy it from the mecca app backups output.

Restore replaces all data

The --force flag skips the confirmation prompt. A restore replaces the entire database with the bundle contents. Always create a fresh backup before restoring if you need to preserve current data.

Loading a dump from elsewhere

To restore a pg_dump you produced outside AppMecca, use the dashboard's Database tab (open the app, choose the file, confirm). A custom-format dump (pg_dump -Fc) is strongly preferred.

See Database Operations for the full command reference.

Key Takeaways

  • Add database: enabled: true to your appmecca.yaml -- that is all the AppMecca-specific configuration needed
  • AppMecca injects DATABASE_URL, DB_HOST, DB_PORT, DB_NAME, DB_USER, and DB_PASSWORD automatically
  • The database and user names are generated — read them from the environment, never construct them
  • Use an entrypoint script that waits for the database, runs migrations, then starts your app
  • Guard migrations with APPMECCA_SERVICE to prevent duplicate runs when you add workers later
  • Back up your app before deploying schema changes

What's Next?