Operations 10 min read

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.

Code of Duty
Code of Duty
Code of Duty
From Conda to Docker to Global Nginx: A Maintainable Deployment Architecture for Personal Projects

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 Container

The 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-app

This 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockercontainerizationNginxHTTPSDocker ComposeDeployment ArchitectureServer OperationsMulti-project Hosting
Code of Duty
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.