Launching a django webserver in Docker
Tags: docker, python, django, poetry
Published: 2023-12-05 22:54:14

---
title: Launching a django webserver in Docker
published: true
description: There are a couple things I forget every time I do this, so I'm writing a blog post/note to my future self
tags: docker, python, django, poetry
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-05 22:13 +0000
---

Every single time I have to set up Docker for a new web server I forget a couple key things on how to set it up. This little post is mostly a note to my future self that this is how you do it and why.

For a detailed breakdown of common keywords and concepts of putting together a Dockerfile, check out my previous post on [Dockerizing a simple python process](https://dev.to/thejessleigh/dockerizing-a-simple-python-process-2gdk).

I found myself needing to run a django server on Docker today because it turns out that you can't set memory limits using the python `resource` library on processes on macOS since about version 10.14. Solution? Run it on a Python Docker Image that runs on Linux. Easy enough.

```docker
FROM python:3.11
RUN pip install poetry
COPY . .
RUN poetry install
CMD ["python", "./myapp/manage.py", "runserver"]
```

Quick and dirty. I could probably be more careful with what I'm copying, where, and such. But I'm in a rush and this will do the job.

```bash
docker build . -t myapp
```

Builds just fine, I see poetry installing the packages that it should. Beautiful!
However, when I try to run the image...

```bash
docker run -it -p 8000:8000 myapp
Traceback (most recent call last):
  File "//./myapp/manage.py", line 11, in main
    from django.core.management import execute_from_command_line
ModuleNotFoundError: No module named 'django'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "//./myapp/manage.py", line 22, in <module>
    main()
  File "//./myapp/manage.py", line 13, in main
    raise ImportError(
ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?
```

That's annoying. I just saw poetry install all the libraries. And yes, I went and checked, and `django` is present in my `poetry.lock` and `pyproject.toml` files. What gives? Seems like poetry is creating a virtualenv somewhere that needs to be activated, but that's really not necessary since Docker is already isolating the project and having a virtualenv is redundant. So let's try add a poetry config command to the Dockerfile.

```docker
FROM python:3.11
RUN pip install poetry
COPY . .
RUN poetry config virtualenvs.create false --local
RUN poetry install
CMD ["python", "./cohortcreation/manage.py", "runserver"]
```

Now, the --local tag isn't strictly necessary here, because, again, Docker is isolating this project. But just in case I copy paste this somewhere where it might mess with poetry configurations, let's make sure this config value stays local to whatever project I'm working with.

Next, rebuild the docker image. Loads just fine. Let's try running it.


```bash
docker run -it -p 8000:8000 myapp
2023-12-05 22:33:32,585 INFO autoreload django.utils.autoreload Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
December 05, 2023 - 22:33:32
Django version 4.2.4, using settings 'myapp.project.settings'
Starting ASGI/Daphne version 4.0.0 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
2023-12-05 22:33:32,806 INFO server daphne.server HTTP/2 support not enabled (install the http2 and tls Twisted extras)
2023-12-05 22:33:32,806 INFO server daphne.server Configuring endpoint tcp:port=8000:interface=127.0.0.1
2023-12-05 22:33:32,807 INFO server daphne.server Listening on TCP address 127.0.0.1:8000
```

Hey! That looks like what I'd expect from running `./manage.py runserver` on an average django project. Success! Now I just need to open my browser and get on with my testing.

![Empty response from localhost oh no!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqciffznluvfzt397695.png)

What? No. Why? I set up the ports when running the container. I even remembered this time to add the `-p 8000:8000` flag to the run command the first time! I even remembered that the first port listed is for what port you want to access on the host, and the second port is the port you want Docker to forward, even though that always feels backwards to me.

Okay, let's check the container and see what's up.

```bash
docker ps

. . .

d8ccf5c1aa7a myapp "python ./myapp/ma…" 6 seconds ago Up 5 seconds 0.0.0.0:8000->8000/tcp priceless_pascal
```

That all seems correct. Should be able to access localhost:8000 to get to whatever port 8000 on the docker container is doing. So what's happening here?

I kill the container, run the webserver in a different terminal tab, just to make sure I'm not going crazy. It loads fine. I keep the local server running, but also start up the docker container again, and now I can't access the app front end through the browser anymore. So clearly docker is doing *something* with port 8000. But letting me access my server isn't it.

I'll spare you the roughly three thousand google searches I did before arriving at the answer. Long story ~short~ somewhat less long, Python thinks we're exposing our API at `127.0.0.1:8000`. This is what we want if we're just running the server locally. _However_, if we look at the ports output from `docker ps`, we're not exposing `127.0.0.1:8000`. We're exposing `0.0.0.0:8000`. One last tweak to the Dockerfile should do the trick.

```docker
FROM python:3.11
RUN pip install poetry
COPY . .
RUN poetry config virtualenvs.create false --local
RUN poetry install
CMD ["python", "./myapp/manage.py", "runserver", "0.0.0.0:8000"]
```

Building the image works. Running the image works. Now I just need to cross my fingers and check my browser.

![Django Admin Panel Header](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7opvtnyr5k8qs9vm6cp.png)

Thank god. Time for a nap.