From Conda to Docker to Global Nginx: A Maintainable Deployment Architecture for Personal Projects
The author details their journey from Conda to Docker and a global Nginx gateway to create a maintainable, scalable deployment architecture for personal projects, explaining why Docker Compose organizes internal services while a single Nginx container handles public entry, HTTPS, and multi-project routing via a shared Docker network.
This article is the 25th in the "Deploying Yourself to the Internet" series and summarizes the deployment architecture decisions made from episodes 18 to 24.
Motivation
After securing the server, the author considered project deployment. Deployment is not just pulling code and running a start command; a long‑term project must address application runtime, database, multi‑service startup, project isolation, port exposure, Nginx entry, multi‑project coexistence, configuration migration, and troubleshooting. Without upfront planning, configurations scatter, new projects conflict, and maintenance becomes burdensome.
Day one the project runs; a month later configs scatter; three months later new projects conflict; six months later you forget how you deployed. Eventually you stop wanting to maintain it.
Conda vs Docker
Conda manages Python environments well for local development, data analysis, or temporary scripts. However, a blog is a long‑term project requiring a database, Nginx exposure, HTTPS, and coexistence with a personal site and portfolio. Conda only manages Python environments and does not handle service startup, databases, logs, configuration, environment variables, migration, or multi‑project organization. The author abandoned Conda not because it cannot work, but because it does not match the long‑term goal.
Docker was chosen because it freezes the runtime environment, easing migration. A Dockerfile records the build, an image records the runtime, a container provides isolation, and a Compose file records how a group of services run together. This answers seven critical questions:
1. What services does the project need?
2. Which service is exposed externally?
3. Where is database data stored?
4. Where do environment variables come from?
5. How do services start and stop?
6. How to view logs?
7. How to recover when moving servers?
Docker has a learning curve (images, containers, networks, volumes, port mapping, Compose), but the cost buys a clearer deployment structure.
Docker Compose
A real project often needs multiple containers (app, database, cache, background jobs). Running each with docker run leads to long, unmaintainable commands. Docker Compose organizes internal services: application, database, volumes, environment variables, and networks in a single file, turning the project into a describable service group. Later, the author can see exactly what services exist, how they connect, where data lives, port handling, and startup commands.
Nginx Placement
With multiple projects (blog, personal site, portfolio) on one server, each project exposing its own port or managing its own Nginx and certificates creates chaos and scattered maintenance. The solution: a single global Nginx container that listens on ports 80 and 443, routes by domain, centralizes HTTPS configuration, and reaches project containers via a shared Docker network.
Public User
-> Global Nginx
-> Project App Container
-> Project DB ContainerThe resulting architecture:
Server
├── Global Nginx Container
│ ├── Listens 80 / 443
│ ├── Manages domain config
│ ├── References HTTPS certificates
│ └── Proxies to different projects
│
├── Blog Project
│ ├── app container
│ ├── db container
│ ├── compose.yml
│ └── data volumes
│
├── Site Project
│ ├── app or static service
│ ├── compose.yml
│ └── build artifacts
│
└── Public Docker Network
├── global-nginx
├── blog-app
└── site-appThis architecture solves core problems:
Environment managed by Docker
Multi‑service managed by Docker Compose
Public entry managed by global Nginx
Databases not exposed to the public internet
Projects keep clear boundaries
New projects have a fixed onboarding pattern
Migration has directories and configs to rely on
Summary: Three Pitfalls Avoided
Only looking at whether it runs today. Conda might be the fastest path to launch, but a blog runs for years; the question must be how to maintain it six months later.
Treating every project as a single project. A single blog works with many approaches, but future projects (site, portfolio, tools) need unified entry, port management, and centralized certificates; otherwise the first project's shortcuts become technical debt.
Thinking containerization means putting everything in containers. Docker is useful, but clear boundaries matter: service containers, database containers with persistent volumes, global Nginx container for entry, certificates and configs mounted from host directories. The combination yields a maintainable solution.
The goal shifted from "get the project running" to "run the project in a maintainable, migratable, extensible way." Speed suits throwaway experiments; structure suits a long‑term personal technical base.
Next phase: HTTPS — why personal sites need HTTPS, obtaining and deploying SSL certificates, configuring Nginx for HTTPS, managing multi‑domain certificates with wildcard certs and acme.sh, and handling certificate renewal.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code of Duty
"Code of Duty" — Every line of code has its own mission. We avoid shortcuts and quick fixes, focusing on authentic coding reflections and the joys and challenges of technical growth. The journey of learning matters more than any destination. Join us as we humbly forge ahead in the world of code.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
