How I Built My Python AMA App and Containerized It with Docker
Step-by-step guide on running your own AMA app using Python, Docker, and self-hosted volumes.
Introduction
I recently built a lightweight Ask Me Anything (AMA) application in Python. I decided to containerize it using Docker for better control and portability. Instead of deploying it on cloud, I decided to self-host my own application. In this article, I'll walk you through the containerization process and show how I use Docker to bind ports, pass environment variables, and persist the app's data using volumes.
The AMA App in Python
The AMA application was built to answer submitted questions using Python. Similar to most of the anonymous QnA Application out there. This app will not require any login or registration of the user. The backend handles incoming questions, stores them in a local database, and providing answered question section. The whole application is kept simple as it is just a simple app for me to learn docker and self-hosting which are the main reason.
If you're interested in the source code, I've published it publicly on GitHub:
Why Containerize with Docker?
Docker makes it easier to deploy and run applications across different environments without worrying about dependencies or configuration issues. In my case, it allowed me to run the AMA app on my local Ubuntu machine.
How I Containerized the AMA App
Once I wrote the AMA app in Python, I created a `Dockerfile` to build image and containerize it.
The Dockerfile consist of several Instructions. You can read the full documentation of Docker Instructions here. For a quick look on my Dockerfile and short explaination:
FROM python:3.9-slim
WORKDIR /ama_app
COPY . /ama_app
RUN pip install -r requirements.txt
EXPOSE 2020
CMD ["uvicorn", "main_ama:app", "--host","0.0.0.0", "--port", "2020"]
FROM
- will pull you a python image from the Docker Hub with their version which are separated by the semicolon.
WORKDIR
- is the working directory in your image build later. Any instruction given in your Dockerfile will follow this directory.COPY
- this instruction is to copy files and directory. It will copy everything (the dot symbol) from my machine working directory into the docker working directory.RUN
- this instruction is to run a specific build image command. When building the image, it install all the requirements needed for my application to work properly.EXPOSE
- declares the port that the application will listen on. It doesn't actually publish the port to the public. You need to do that when running the container. This actually will help in terms of documentation.CMD
- this is the default command when running the container. here I bind the application to my localhost with port 2020
Once your Dockerfile is ready. You can build the application with the build command
docker build -t IMAGE_NAME .
It will take some time depending on the size of the image that you are building. Especially since I have to install all the requirements using pip. You can actually try to use UV to install all the requirements and can see the difference. After that, to run the image into a container, you can run the following command:
docker run -p 2020:2020 --env-file .env -v $(pwd)/database:/ama_python/database python-ama
Here's what this command does:
-p 2020:2020
- Maps port 2020 on the host to port 2020 in the container so I can access the app vialocalhost:2020
.--env-file .env
- Loads environment variables (like API keys, settings and senstivie information) from a local.env
file and passes them into the container.-v $(pwd)/database:/ama_python/database
- Mounts the host's `./database` directory into the container. This ensures all the app's data (like sqlite db) is stored persistently outside the container.python-ama
- The name of the Docker image built for this app.
This setup allows me to keep app data even if the container is removed or restarted. It also keeps secrets secure by separating them in a `.env` file. Now, when the container is up and running, you can actually access the application through your localhost with the published port.
Cloudflare Tunnel
So, I have an application but it only can be accessed within my localhost and people in the same network with me. How am I going to share my AMA app with friends so that they can ask questions or send anonymous feedback? This is where Cloudflare Tunnel comes in. It's free, but you'll need to own a domain. You can buy one cheaply from providers like Namecheap, Porkbun, GoDaddy, or even directly from Cloudflare. I bought mine from Namecheap.
To use Cloudflare Tunnel, follow these steps:
-
Set up your domain with Cloudflare:
First, register your Cloudflare account and add your domain. Once done, go to your domain provider (e.g., Namecheap) and change the nameservers to the ones provided by Cloudflare. This will allow Cloudflare to manage your DNS. -
Install cloudflared:
Open your terminal and run the following commands to installcloudflared
:
If it doesn't work, you can follow the latest instructions from the official Cloudflare Tunnel docs.sudo apt install cloudflared
-
Authenticate cloudflared with Cloudflare:
Run this command:
This will open a browser asking you to select your domain. After approving it, the credentials will be saved incloudflared login
~/.cloudflared/
. -
Create your tunnel:
Next, you can create a tunnel and give it a name:
This will generate a tunnel ID and credentials file in json undercloudflared tunnel create python-ama
~/.cloudflared/
. The credentials file will have similar name to the tunnel ID such as xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json -
Create a configuration file:
Create a config file that tells Cloudflare how to route traffic. For example, create a YAML file like this:
Paste the following content and adjust accordingly:sudo nano /path/to/.cloudflared/python-ama-config.yaml
Thetunnel: python-ama credentials-file: /path/to/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json ingress: - hostname: yourdomain.com service: http://localhost:2020 - service: http_status:404
tunnel: python-ama
is the name of the cloudflare tunnel that you have create earlier. Replace thehostname
with a subdomain of your own domain, and make sure it points to your local port (2020, in this case). The credentials file path should match the JSON file Cloudflare created. -
Add a DNS record:
You can add the record from the Cloudflare Dashboard that have yourdomain.com. In my case, I create a CNAME record in my domain Cloudflare DNS settings. Below are the example of me creating the CNAME record with the Cloudflare tunnel configuration. -
Run the tunnel:
Finally, start your Cloudflare Tunnel by running:
Now, anyone with your custom subdomain (e.g.,cloudflared tunnel --config /path/to/.cloudflared/python-ama-config.yaml run python-ama
https://yourdomain.com
) can access your self-hosted Python AMA application.
That's it! You now have a secure, public URL for your own app without needing to open any ports on your router. This is how I self-hosted my python AMA application and here is my python AMA application: (https://ask-irfan.site)
Contact Me
If you have questions or would like to be in touch whether to improve the project (or want to collaborate), feel free to reach out. I'm also open to learn from others.