<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alexandre Jacques</title>
    <description>The latest articles on Forem by Alexandre Jacques (@ale_jacques).</description>
    <link>https://forem.com/ale_jacques</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F175155%2F9d53b73c-bc4a-44a3-a003-41e178b985d4.jpg</url>
      <title>Forem: Alexandre Jacques</title>
      <link>https://forem.com/ale_jacques</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ale_jacques"/>
    <language>en</language>
    <item>
      <title>Django (DRF) 12 Factor App with examples</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:11:27 +0000</pubDate>
      <link>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-14cd</link>
      <guid>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-14cd</guid>
      <description>&lt;p&gt;&lt;a href="https://alexandremjacques.com/django-drf-12-factor-app-with-examples" rel="noopener noreferrer"&gt;Django (DRF) 12 Factor App with examples&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>web3</category>
      <category>airdrop</category>
      <category>announcement</category>
    </item>
    <item>
      <title>Django + Unpoly = ️</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:10:23 +0000</pubDate>
      <link>https://forem.com/ale_jacques/django-unpoly--15d6</link>
      <guid>https://forem.com/ale_jacques/django-unpoly--15d6</guid>
      <description>&lt;p&gt;&lt;a href="https://alexandremjacques.com/django--unpoly--love" rel="noopener noreferrer"&gt;Django + Unpoly = ❤️&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Why I use Django services layer</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:08:35 +0000</pubDate>
      <link>https://forem.com/ale_jacques/why-i-use-django-services-layer-4cb1</link>
      <guid>https://forem.com/ale_jacques/why-i-use-django-services-layer-4cb1</guid>
      <description>&lt;p&gt;&lt;a href="https://alexandremjacques.com/why-i-use-django-services-layer" rel="noopener noreferrer"&gt;Why I use Django services layer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>webmonetization</category>
    </item>
    <item>
      <title>Override Django 4 template forms widgets</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:07:33 +0000</pubDate>
      <link>https://forem.com/ale_jacques/override-django-4-template-forms-widgets-5dk6</link>
      <guid>https://forem.com/ale_jacques/override-django-4-template-forms-widgets-5dk6</guid>
      <description>&lt;p&gt;&lt;a href="https://alexandremjacques.com/override-django-4-template-forms-widgets" rel="noopener noreferrer"&gt;Override Django 4 template forms widgets&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Django + Tailwind setup</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:06:45 +0000</pubDate>
      <link>https://forem.com/ale_jacques/django-tailwind-setup-5aek</link>
      <guid>https://forem.com/ale_jacques/django-tailwind-setup-5aek</guid>
      <description>&lt;p&gt;&lt;a href="https://alexandremjacques.com/django-tailwind-setup" rel="noopener noreferrer"&gt;Django + Tailwind setup&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Django (DRF) 12 Factor App with examples</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Fri, 10 Sep 2021 00:55:36 +0000</pubDate>
      <link>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-3i91</link>
      <guid>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-3i91</guid>
      <description>&lt;p&gt;I've been working with Django (especially with DRF) for a while now. Deploying Django apps is the one thing that bothers me the most. It's hard and does not have a pattern. So I decided to build a guide here to keep notes for myself and that may help someone that is struggling with the lack of patterns out there.&lt;/p&gt;

&lt;p&gt;You can check the completed article here: &lt;a href="https://sourcecoded.dev/12-factor-apps-django-with-examples.html"&gt;https://sourcecoded.dev/12-factor-apps-django-with-examples.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>12factorapp</category>
      <category>python</category>
      <category>security</category>
    </item>
    <item>
      <title>Django (DRF) 12 Factor App with examples</title>
      <dc:creator>Alexandre Jacques</dc:creator>
      <pubDate>Sat, 18 Apr 2020 19:36:56 +0000</pubDate>
      <link>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-36jg</link>
      <guid>https://forem.com/ale_jacques/django-drf-12-factor-app-with-examples-36jg</guid>
      <description>&lt;p&gt;I've been working with Django (especially with DRF) for a while now. Deploying Django apps is the one thing&lt;br&gt;
that bothers me the most. It's hard and does not have a pattern. So I decided to build a guide here to keep notes for&lt;br&gt;
myself and may help anyone that is struggling with the lack of patterns out there.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is and why 12 Factors App?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://12factor.net/"&gt;The Twelve-Factor App&lt;/a&gt; is a methodology for building SaaS apps. The main concern is to keep&lt;br&gt;
everything as isolated and as secure as possible. It was first presented by Heroku in 2011 and is still referenced as a&lt;br&gt;
good guide for best practices in building and deploying apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools used on this guide:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Gitlab CI&lt;/li&gt;
&lt;li&gt;VIM (my main editor)&lt;/li&gt;
&lt;li&gt;Django and friends (special mention to python-decouple)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assumes the bellow project structure (that I try to keep for all my projects)&lt;/p&gt;

&lt;pre&gt;
├── Dockerfile
├── apps
│   ├── app1
│   └── app2
├── config
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── docker-compose.yml
├── docker-entrypoint.sh
├── manage.py
├── requirements
│   ├── dev-requirements.in
│   └── requirements.in
└── requirements.txt
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, without further ado, to the guide!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each factor title has a link to its explanation on the original site. Remember: all things explained here can be&lt;br&gt;
ported to other CI/CD environments and to other languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/codebase"&gt;1. Codebase - One codebase tracked in revision control, many deploys&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;My approach is to use &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;Gitflow&lt;/a&gt;. There are some variations on&lt;br&gt;
this but, since Gitflow envisions different branches for different phases of your app, it's easy to create &lt;code&gt;git hooks&lt;/code&gt;&lt;br&gt;
to start a build and/or a deploy.&lt;/p&gt;

&lt;p&gt;Since I'm using Gitlab CI, hooks are "translated" to the &lt;code&gt;only&lt;/code&gt; tag on &lt;code&gt;.glitlab-ci.yml&lt;/code&gt; configuration file. Examining&lt;br&gt;
the example file bellow, notice the &lt;code&gt;only&lt;/code&gt; tag. It informs Gitlab CI to only execute this task on commits to the&lt;br&gt;
branches list:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: gitlab/dind

stages:
  - build
  - deploy

run_build:
  stage: build
  only:
    - master
    - develop

run_deploy:
  stage: deploy
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/dependencies"&gt;2. Dependencies - Explicitly declare and isolate dependencies&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Recently I migrated my projects to &lt;code&gt;pip-tools&lt;/code&gt;. Before the migration I was using &lt;code&gt;pip&lt;/code&gt; but the workflow for managing&lt;br&gt;
development/production dependencies was not working for me. So now, I have a &lt;code&gt;/requirements&lt;/code&gt; directory on my project&lt;br&gt;
root with &lt;code&gt;dev-requirements.in&lt;/code&gt; and &lt;code&gt;requirements.in&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first line of my &lt;code&gt;dev-requirements.in&lt;/code&gt; file includes &lt;code&gt;requirements.in&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-r requirements.in
flake8
isort
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, for local development I can run:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip-compile requirements/dev-requirements.in -o ./requirements.txt
pip-sync
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And have both dev/production dependencies. In production, my pipeline has a slightly different &lt;code&gt;pip-compile&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip-compile requirements/requirements.in -o requirements.txt
pip-sync
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And there you have: no development dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/config"&gt;3. Config - Store config in the environment&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Most of the "juice" of this workflow relies on this item. I have struggled for a while to find some kind of solution for&lt;br&gt;
Django &lt;code&gt;settings.py&lt;/code&gt; environment separation. The great majority involves different files for each environment. But those&lt;br&gt;
solutions always ends up with sensitive information (&lt;code&gt;SECRET_KEYs&lt;/code&gt;, database passwords, third-party app keys, etc.) in your version&lt;br&gt;
control system.&lt;/p&gt;

&lt;p&gt;As I mentioned in the begging of this guide, &lt;code&gt;python-decouple&lt;/code&gt; comes to the rescue! &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are other packages that does similar job - it's just a personal preference to use &lt;code&gt;python-decouple&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The way Decouple works is simple: try to find the declared &lt;code&gt;KEYS&lt;/code&gt; on the following places in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ini&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Default argument passed to config&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, to illustrate, take this &lt;code&gt;config/settings.py&lt;/code&gt; file excerpt:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os

from decouple import Csv, config

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
SECRET_KEY = SECRET_KEY = config('SECRET_KEY')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have a &lt;code&gt;DEBUG=True&lt;/code&gt; in your environment (exported on your Linux/macOS system or declared in your Windows preferences), &lt;code&gt;python-decouple&lt;/code&gt; will read it, parse it to &lt;code&gt;boolean&lt;/code&gt; and inject it&lt;br&gt;
in your &lt;code&gt;settings.py&lt;/code&gt;. If it cannot find the &lt;code&gt;DEBUG&lt;/code&gt; key in you environment, it will try to look for it in a &lt;code&gt;.env&lt;/code&gt; (or &lt;code&gt;.ini&lt;/code&gt;) file.&lt;/p&gt;

&lt;p&gt;In development, I keep an &lt;code&gt;.env&lt;/code&gt; file in the root of my project. The &lt;code&gt;.env&lt;/code&gt; file format is just key/value pairs:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
SECRET_KEY=secret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In production, I take advantage of Gitlab's CI environment settings. It allows me to declare the same key/value pairs&lt;br&gt;
and make it available to the build environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q08hrXgd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/%7Bstatic%7D/images/gitlab_env_variables.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q08hrXgd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/%7Bstatic%7D/images/gitlab_env_variables.png" alt="Gitlab's CI environments variable example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, no database passwords, secrets or app keys ends up on your source control (to avoid deploying your &lt;code&gt;.env&lt;/code&gt;&lt;br&gt;
files do production, add it to your &lt;code&gt;.gitignore&lt;/code&gt; - even though it wouldn't be a big deal as &lt;code&gt;python-decouple&lt;/code&gt; gives precedence to&lt;br&gt;
environment variables).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/backing-services"&gt;4. Backing services - Treat backing services as attached resources&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Implementing #4 is easy if you implement #3. Since every external configuration, URL address, port binding, etc. should&lt;br&gt;
be in environment variables (or &lt;code&gt;.env&lt;/code&gt; file, for that matter), it's already treated as an attached resource. It's quite&lt;br&gt;
simple to verify that, in my case. I just push code to my remote master branch and wait the Gitlab CI do its job. No need to change&lt;br&gt;
anything in code or config files. Just push code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/build-release-run"&gt;5. Build, release, run - Strictly separate build and run stages&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As described in factors #1 and #3, part of this problem is already solved. Still, there's an inherent problem since I'am&lt;br&gt;
using Docker/docker-compose to deply my apps. Due to Docker way of doing things, passing environment variable values to&lt;br&gt;
the build process is a little tricky.&lt;/p&gt;

&lt;p&gt;The secret is knowing how Dockerfile and docker-compose &lt;code&gt;environment&lt;/code&gt; tag works.&lt;/p&gt;

&lt;p&gt;In production, I don't use &lt;code&gt;virtualenv&lt;/code&gt;. It wouldn't make much sense since, by definition, a container is aleady an isolated&lt;br&gt;
"environment". Given that, my &lt;code&gt;Dockerfile&lt;/code&gt; is usually pretty straight forward:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.8

ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1

RUN mkdir /code
WORKDIR /code/
ADD . /code/

RUN pip install --upgrade pip &amp;amp;&amp;amp; \
    pip install pip-tools &amp;amp;&amp;amp; \
    pip-compile -q /code/requirements/requirements.in --output-file /code/requirements.txt &amp;amp;&amp;amp; \
    pip install --no-cache-dir -r /code/requirements.txt

EXPOSE 8001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For everything to work right, my &lt;code&gt;docker-compose.yml&lt;/code&gt; file looks like the following:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'

services:
  api:
    image: image_name
    build: .
    ports:
      - "8001:8001"
    volumes:
      - /var/www/sample_project/media:/code/media
    entrypoint: sh docker-entrypoint.sh
    environment:
      - DJANGO_SETTINGS_MODULE=config.settings
      - SECRET_KEY=${S_KEY}
      - DEBUG=${DEBUG}
      - ALLOWED_HOSTS=${ALLOWED_HOSTS}
      - DATABASE_NAME=${DB_NAME}
      - DATABASE_USER=${DB_USER}
      - DATABASE_PASSWORD=${DB_PASSWORD}
      - DATABASE_HOST=${DB_HOST}
      - DATABASE_PORT=${DB_PORT}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That means: docker-compose reads from the environment variables set in Gitlab CI the value of, for example,&lt;br&gt;
&lt;code&gt;${S_KEY}&lt;/code&gt; and sets it on the Docker build environment under the key &lt;code&gt;SECRET_KEY&lt;/code&gt;. It happens that, on my&lt;br&gt;
&lt;code&gt;settings.py&lt;/code&gt; file, its the exact key &lt;code&gt;python-decouple&lt;/code&gt; looks for.&lt;/p&gt;

&lt;p&gt;To simplify even more:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CI environment variable --&amp;gt; Docker enviroment variable --&amp;gt; &lt;code&gt;settings.py&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To complete the process and follow the factor #5 premisses, your build pipeline should make a TAG on your source control&lt;br&gt;
before cloning/checkout the code and build it. This way you could track how and when a release was generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/processes"&gt;6. Processes - The app is executed in the execution environment as one or more processes.&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This one has a little to do on how we build the app and much more on how we architect it. As I mentioned before, I&lt;br&gt;
mainly work with DRF (Django REST Framework) to build REST APIs for web and mobile clients.&lt;/p&gt;

&lt;p&gt;This usually means that I rely on JWT tokens to keep "sessions" on my apps. Since no sticky sessions are needed and&lt;br&gt;
my APIs don't have a meaninful state, I'm covered on this item. But, in case I needed some kind of server-side state, I&lt;br&gt;
once relied on Redis to do that. Just remember to keep Redis server address, port and credentials on your configuration&lt;br&gt;
environment (&lt;code&gt;.env&lt;/code&gt; file and/or environment variables).&lt;/p&gt;

&lt;p&gt;That all means that, if needed, this whole setup can scale horizontaly by just spanning more processes (containers in&lt;br&gt;
this case).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/port-binding"&gt;7. Port binding - Export services via port binding&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As seen on my &lt;code&gt;Dockerfile&lt;/code&gt; and my &lt;code&gt;docker-compose.yml&lt;/code&gt; files, I export the &lt;code&gt;8001&lt;/code&gt; port to the operational system. That&lt;br&gt;
means my services are accesible through that port. Although its possible and valid keep things this way, its usual to&lt;br&gt;
have a proxy (a reverse proxy) in front of my services.&lt;/p&gt;

&lt;p&gt;For that I have to configure 2 things: a gunicorn WSGI server and a Nginx proxy.&lt;/p&gt;

&lt;p&gt;The first one, the Gunicorn server, is configured by the &lt;code&gt;docker-entrypoint.sh&lt;/code&gt; script (notice that this script is&lt;br&gt;
called by the &lt;code&gt;docker-compose.yml&lt;/code&gt; on &lt;code&gt;entrypoint&lt;/code&gt; tag):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
python manage.py collectstatic --noinput
python manage.py migrate

gunicorn -b 0.0.0.0:8001 -w 4 config.wsgi:application
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that, on execution, docker-compose builds the container and run this script to start the container execution.&lt;br&gt;
Gunicorn in binding on port &lt;code&gt;8001&lt;/code&gt; which is exposed to the host operational system on the same port. If needed, we could&lt;br&gt;
change that by changing the &lt;code&gt;ports&lt;/code&gt; on &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The second step is to configure a proxy server. My setup already have a Nginx server running on an VPS instance and its&lt;br&gt;
not containerized. That's by design since I can move the API container to a cloud provider and have the another kind of&lt;br&gt;
reverse proxy pointing to my API container (and that's why my docker-compose don't start a proxy service inside the&lt;br&gt;
container).&lt;/p&gt;

&lt;p&gt;Configuring a Nginx as a reverse proxy is very straight forward:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
   listen 80;
   listen [::]:80;
   server_name api.server.com;

   location / {
       proxy_read_timeout 360;
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_pass http://127.0.0.1:8001;
   }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that's it. Now requests made to port &lt;code&gt;80&lt;/code&gt; gets proxied to &lt;code&gt;8001&lt;/code&gt; (the container!).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/concurrency"&gt;8. Concurrency - Scale out via the process model&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As stated before, this whole setup is based on Docker and is stateless. That means its scalable via processes. I'm not&lt;br&gt;
an expert on cloud but, I bet that with minimal changes to port bindings (and network definitions on containers), this setup can run on Kubernetes or some sort&lt;br&gt;
of container manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/disposability"&gt;9. Disposability - Maximize robustness with fast startup and graceful shutdown&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Again, since factor #8 is covered, this one is easy. Kill and restart the process or any container process and you still have a&lt;br&gt;
new container ready to receive requests. Of course this spawning of containers must be managed by some kind of container&lt;br&gt;
manager.&lt;/p&gt;

&lt;p&gt;Just have to check how Gunicorn manages SIGTERM graceful finalizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/dev-prod-parity"&gt;10. Dev/prod parity - Keep development, staging, and production as similar as possible&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I don't use Docker on my development machine. But I could as easily as in production. In fact, during the process of&lt;br&gt;
building this setup, I had to validate my ideas and configurations. And, for that, I relied on running Docker and&lt;br&gt;
docker-compose locally.&lt;/p&gt;

&lt;p&gt;Notice that my database is not started inside the container as well. That's also by design. My production setup has a&lt;br&gt;
dedicated database server machine. And to keep the environment parity, I also have a database installation on my local&lt;br&gt;
machinei (but this could be another local container - I just happen to have an old installation already running). This &lt;br&gt;
keeps things as similar as possible to my production environment and my configuration "compliant" with factor #4.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/logs"&gt;11. Logs - Treat logs as event streams&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;That's one missing piece on my setup. I just don't log. I mean, on my development machine I log some stuff for&lt;br&gt;
debugging purposes. In production, I don't have any kind of application logs. I can, in fact, peek inside de container&lt;br&gt;
and see the container and Gunicorn logs, but that's all.&lt;/p&gt;

&lt;p&gt;Ideally I should use a third-party log service but, for now, it's still too expensive for my budget.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://12factor.net/admin-processes"&gt;12. Admin processes - Run admin/management tasks as one-off processes&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This one I achieve with the help of Docker. We can easily run REPL commands inside the container, e.g.:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it &amp;lt;container id&amp;gt; python manage.py makemigrations
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It may vary depending on the chosen Docker image but the idea is that, even with a leaner image (like an *-alpine one),&lt;br&gt;
you can install the needed tools changing the &lt;code&gt;Dockerfile&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I find it nice to have some kind of best practices already defined and battle tested. Applying it to my stack was a&lt;br&gt;
great way to learn some stuff new and useful. Its definitly not the end of it. Sure I can evolve this to something&lt;br&gt;
better.&lt;/p&gt;

&lt;p&gt;And by putting it together I hope I can help, or give some ideas, to someone it trouble trying to figure some of this&lt;br&gt;
out.&lt;/p&gt;

</description>
      <category>django</category>
      <category>rest</category>
    </item>
  </channel>
</rss>
