Learning Docker has been on my to-do list for a while. When I say "a while", I mean it. (Circa 2017, or even earlier?)

I won't list the advantages of containerization here (as Google can tell you more), but I'm a practical learner. Which means I learn things by actually doing it. Back in 2017, I started this Telgram catbot project to share the love of my two cats with the world (side goals: improve my Python coding skills/learn Git). It was originally deployed on Heroku; given that I was on the free "development" tier and the popular demand (mostly from my friends), my "dyno hours" (aka available resources) quickly exhausted every month. I had to shut down the project last year so that my other projects could stream normally on Heroku. When I did the portfolio refresh this year, I realized that VPS is an ideal place to host my projects; it gives me more freedom than Heroku does (plus, I've paid for it every month already). So which one should I migrate first? Catbot, my first and foremost (serious) project, becomes the top choice.


I won't spend too much time describing the efforts it took on reactivating the catbot project itself, as it's not too relevant. I do want to mention, though, that the bot was written in mid-2017 with Python 2.7. (I don't know what I was thinking back then, either - I didn't fully embrace Python 3 until early 2018 though) Also, the Python-telegram-bot framework recently received a major update on callback (now context-aware). Heroku only supports PostgreSQL, but with Ghost my server already has MySQL nicely set up. Moreover, Heroku recycles my applications once per day, but that won't be the case for my server, so I need to have the job set up correctly for the daily push to users. Took me a day to fix all these nuances, and I had the bot up and running in the local mode!

I'm now ready to embrace Docker. However, I have no experience other than going through the official Docker starter doc once. That did not impress me, as I vaguely remember terms as "images", "hub", etc. What should I do?

I'd like to give credits to people on Twitter, though: below is a list of useful resources provided by them:
Production-ready Docker packaging
Docker - 从入门到实践 (Note: zh-cn only)
Also Google! :)

Somehow the two terms, "yaml" and "Dockerfile", came to my mind first. What do they mean? Do I need both to run my bot? In a nutshell, the bot depends on a Python script, which communicates with a local database that persists user information. (Since version 12, Python-telegram-bot natively supports persistence. The only example is about persisting conversational handlers, which is irrelevant to my case. I browsed some other posts online, and they all suggested sticking with databases for persistence. So I did not break the tie with databases) Besides, the bot talks to Cloudinary and needs a Telegram bot token passed from the environment. Thus, I need to take all API secrets into consideration as well.

I chose to start with "yaml", which is Docker composer. :)

Challenge 1: cannot run docker-compose up

ERROR: Version in "./docker-compose.yml" is unsupported. You might be seeing this error because you're using the wrong Compose file version. Either specify a version of "2" (or "2.0") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.
For more on the Compose file format versions, see

I did not have docker-composed install on my sandbox originally, so I followed the command prompt as it suggested me to grab the app through apt-get. Then I tried docker-compose up again, and it returned the error as shown above. I could change the version from 3 to 2 and make it work, but I wonder why. I have Docker version == 19.03.8, which is supposed to support version 3. This comment helped me out, seems that I didn't get docker-compose installed correctly through apt-get.

Challenge 2: ... my container exits itself?

Yes, it always quitted with exit code 0. I'm following the example here, but my yaml file looks like below:

version: "3.0"

    image: python:3.6-slim-buster
    env_file: env.list

... it just exited. Not until now did I realize that I started an empty container with nothing in it. Thus it gracefully exited. Since my app is a simple one and does not require multi-container interactions, I decided to give Dockerfile a try.

Challenge 3: I really don't know how Docker works.

I adopted the "somewhat better image" example in this post as a starting point and made some modifications. Of course, I did not get it right the first try. After trials and errors, I believe the groundwork is finally done. What's next?

Well, I then managed to run docker build. Looks like this command somehow turns my configuration into an image that is replicable across different machines.

The build succeeded. What does that mean? After couple hours of googling, I found that I should run docker run. There are some flags that I have to be mindful about.

  • I'd like the container appears to be running on the host itself (from the perspective of the network) so it could seamlessly connect to the localhost database, so I added --net=host
  • There are too many secrets to pass in single run command, therefore it's loaded from a file called env.list: --env-file=env.list
  • My app will not quit itself easily. I would prefer it running silently in the background while I'm working on some other stuff. Thus, --detach.
  • Even after I reboot my server, the container should come back to life ASAP. I updated the restart status afterward, but could have done so in the run command by adding --restart=unless-stopped

This seems to be a long run command to me, but I'm pretty sure there are longer ones out there (maybe why people eventually choose to use composer to handle all these?). After running docker run --detach --env-file=env.list --name NAME --net=host BUILD, I checked the status of the container and boomed! Everything is up and running.


Sometimes, a picture worths more than a thousand words:


I tried restarting my server a couple of times, and the cat bot is still up and running :O. You are more than welcomed to test it out here (A valid Telegram account required). Based on the logic, the daily push is supposed to be functioning. We will see how it goes tomorrow.

The original title is "My (Tough) First-try with Docker". I was still unclear about docker compose and Dockerfile back then. I feel much better now though after actually building an image and starting a container myself. It only took me half day (less than 4 hours)! I can see it comes in handy for reproducible machine learning projects (passing my beautifully boxed DL codes to other DS) without the hassle of manual setup. Also, I love the restart option - no more tmux session gone after system restart!!

Hope you enjoy this little post (with grumbles) and stay safe during this uncertain time. It is said that staying at home is an ideal time to pick up new skills; but still, physical health comes first. Working on resurrecting legacy projects does bring joys to me during this remorseful time, though.