- GitHub for code
- Heroku pipeline with one production app
- Heroku Postgres
- Heroku Redis
- Linked to GitHub repo
- Add environment variables for
ENVIRONMENT=live,DISABLE_COLLECTSTATIC=1, andSECRET_KEY - Add
pgbouncerandnginxbuildpacks
- Cloudflare for SSL and CNAME pointing to Heroku app domain
- Force SSL with a Cloudflare rule
- Namecheap for the domain
- Point nameservers to Cloudflare
- Sentry for error logging
- Panelbear and Plausible for privacy-focused analytics
- UptimeRobot to get alerts for when the site goes down
-
-
Save ArchTaqi/2c571f21c837abb5f5e9f47ac8764e8a to your computer and use it in GitHub Desktop.
Settings, files, and a checklist to deploy a Django app to Heroku with nginx + gunicorn + postgres + redis and using `poetry` for dependencies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def when_ready(server): | |
| # touch app-initialized when ready | |
| open("/tmp/app-initialized", "w").close() | |
| bind = "unix:///tmp/nginx.socket" | |
| workers = 3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| rem this lives in the `bin` directory (i.e. bin/post_compile) | |
| #!/usr/bin/env bash | |
| set -eo pipefail | |
| indent() { | |
| sed "s/^/ /" | |
| } | |
| puts-step() { | |
| echo "-----> $@" | |
| } | |
| /app/.heroku/python/bin/python -m pip install --upgrade pip | |
| puts-step "Installing dependencies with poetry..." | |
| poetry config virtualenvs.create false 2>&1 | indent | |
| poetry install --no-dev 2>&1 | indent | |
| puts-step "Collect static files (part 1)..." | |
| python manage.py collectstatic --noinput 2>&1 | indent | |
| puts-step "Compress files..." | |
| python manage.py compress --force 2>&1 | indent | |
| puts-step "Collect static files (part 2)..." | |
| python manage.py collectstatic --noinput 2>&1 | indent |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| web: python manage.py collectstatic --noinput && bin/start-pgbouncer bin/start-nginx gunicorn -c gunicorn.conf.py project.wsgi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| poetry==1.1.6 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| python-3.7.12 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| from pathlib import Path | |
| ENVIRONMENT = os.getenv("ENVIRONMENT", "dev") | |
| # Regular dev settings go here | |
| if ENVIRONMENT == "live": | |
| import dj_database_url | |
| import sentry_sdk | |
| from sentry_sdk.integrations.django import DjangoIntegration | |
| sentry_sdk.init( | |
| dsn=os.environ.get("SENTRY_DSN"), | |
| integrations=[DjangoIntegration()], | |
| traces_sample_rate=1.0, | |
| send_default_pii=True, | |
| ) | |
| DEBUG = False | |
| COMPRESS_OFFLINE = True | |
| ALLOWED_HOSTS += [ | |
| "ADD-DOMAINS-HERE" | |
| ] | |
| CACHES = { | |
| "default": { | |
| "BACKEND": "django_redis.cache.RedisCache", | |
| "LOCATION": os.environ.get("REDIS_URL"), | |
| "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, | |
| } | |
| } | |
| DATABASES["default"] = dj_database_url.config(conn_max_age=600) | |
| LOGGING = { | |
| "version": 1, | |
| "disable_existing_loggers": False, | |
| "formatters": { | |
| "console": { | |
| "format": "[%(asctime)s][%(levelname)s] %(name)s " | |
| "%(filename)s:%(funcName)s:%(lineno)d | %(message)s", | |
| "datefmt": "%H:%M:%S", | |
| }, | |
| "verbose": { | |
| "format": ( | |
| "%(asctime)s [%(process)d] [%(levelname)s] " | |
| + "pathname=%(pathname)s lineno=%(lineno)s " | |
| + "funcname=%(funcName)s %(message)s" | |
| ), | |
| "datefmt": "%Y-%m-%d %H:%M:%S", | |
| }, | |
| "simple": {"format": "%(levelname)s %(message)s"}, | |
| }, | |
| "handlers": { | |
| "null": {"level": "DEBUG", "class": "logging.NullHandler"}, | |
| "console": { | |
| "level": "DEBUG", | |
| "class": "logging.StreamHandler", | |
| "formatter": "verbose", | |
| }, | |
| }, | |
| "loggers": { | |
| "": {"handlers": ["console"], "level": "INFO", "propagate": False}, | |
| "testlogger": {"handlers": ["console"], "level": "INFO"}, | |
| "deploylogs": {"level": "WARNING", "propagate": True}, | |
| }, | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment