The Ultimate Dev Workflow: From Local FastAPI & Due Dev to Production Deployment
In modern web development, the journey from a line of code on your local machine to a live feature serving users is fraught with potential pitfalls. Differences between development and production environments can lead to bugs that only appear after deployment. This guide outlines a robust, three-stage workflow for FastAPI and Vue.js projects that ensures consistency, reliability, and developer productivity, taking you from rapid local development to confident production deployment.
This workflow allows you to:
- Stage 1: Develop Rapidly with hot-reloading for both your frontend and backend.
- Stage 2: Test Reliably in a production-like Docker environment on your local machine.
- Stage 3: Deploy Confidently with a setup that has already been validated.
Let’s build it step by step.
Step 1: Establish a Unified Project Structure
A clean, logical structure is the foundation of a manageable project. This structure is designed to hold all the configuration files for our three-stage workflow.
/your-project
├── .github/
│ └── workflows/
│ └── deploy.yml
├── backend/
│ ├── app/
│ │ └── main.py
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── public/
│ ├── src/
│ ├── package.json
│ ├── pnpm-lock.yaml
│ └── vite.config.js
├── nginx/
│ ├── nginx.conf # Production Nginx config
│ └── Dockerfile.nginx # Multi-stage Dockerfile for Vue + Nginx
├── .env.dev # For Stage 1: Local native development
├── .env.test # For Stage 2: Local Docker testing
├── .env.prod # For Stage 3: Production deployment (on server)
├── .gitignore
├── docker-compose.yml # Base services definition for Docker
└── docker-compose.override.yml # Development-specific Docker settings
Step 2: Master Environment Variables with .env
Files
Managing secrets and environment-specific settings is critical. We’ll use a separate .env
file for each stage.
Crucial First Action: Add .env*
to your .gitignore
file immediately. Never commit secrets or environment configurations to your Git repository.
Environment Files Explained
.env.dev
(Local Development): For running services directly on your machine.
# For FastAPI running on host
DATABASE_URL=postgresql://user:password@localhost:5433/myapp_dev SECRET_KEY=a_secret_for_local_dev
# For Vue running on host, points to local backend
VITE_API_BASE_URL=http://localhost:8000
(We use port 5433
for the database to avoid conflicts with the Docker setup.)
.env.test
(Local Docker Testing): For orchestrating containers on your machine.
# For Docker services
POSTGRES_DB=myapp_test
POSTGRES_USER=user
POSTGRES_PASSWORD=password_for_docker_test
SECRET_KEY=a_secret_for_docker_test
.env.prod
(Production): This file should only exist on your production server.
# For Production deployment
POSTGRES_DB=myapp_prod
POSTGRES_USER=prod_user
POSTGRES_PASSWORD=a_very_strong_production_password SECRET_KEY=a_unique_and_strong_production_secret_key
Stage 1: Fast Local Development Loop
This is your day-to-day mode, optimized for speed and instant feedback with hot-reloading.
How to Run the Backend (FastAPI + Uvicorn)
Setup Python Environment: Navigate to your backend directory and create a virtual environment.
cd backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Run the Uvicorn Server: From the backend
directory, start the server, telling it where to find the development environment file.
uvicorn app.main:app --reload --env-file ../.env.dev
Your FastAPI backend is now live at http://localhost:8000
and will automatically restart when you save a file.
How to Run the Frontend (Vue + Vite)
Install Dependencies: Navigate to the frontend directory and use pnpm
to install packages.
cd frontend
pnpm install
Run the Vite Dev Server:
There is couple of changes you need to make to the frontend configuration. First of all Vite server in development looks for the .env.development
file. To make it use .env.dev
you need to change the package.json
file as follows:
// frontend/package.json
{
"name": "my-vue-app",
"version": "0.0.0",
"private": true,
"scripts": {
// Change this line
"dev": "vite",
// To this
"dev": "vite --mode dev",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
// ...
}
}
And in the proposed setup the environment files live in the root of the whole project while Vite will look for the files in the frontend
directory. To make it look in the root you need to change vite.config.mjs
as follows:
// frontend/vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
// This is the line you need to add
envDir: '../',
plugins: [vue()],
resolve: {
alias: {
// You might have aliases here, they are unaffected
'@': '/src',
}
}
})
Then run:
pnpm dev
Vite’s server will start (usually on http://localhost:5173
), serving your Vue app with hot-module replacement. It will automatically connect to your local backend thanks to the VITE_API_BASE_URL
variable.
Stage 2: Integrated Testing with Docker
After developing features, this stage lets you test the entire application stack as a cohesive unit, running exactly as it will in production.
How to Configure the Docker Images
backend/Dockerfile
: A standard Dockerfile to containerize the FastAPI application.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY1 ./app /app/app
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "app.main:app", "-b", "0.0.0.0:8000"]
nginx/Dockerfile.nginx
: A powerful multi-stage Dockerfile
that first builds the Vue app and then copies the static assets into a lean Nginx image.
# Stage 1: Build Vue.js frontend
FROM node:18-alpine AS builder
WORKDIR /app
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
COPY frontend/ ./
RUN pnpm build
# Stage 2: Setup Nginx production server
FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
How to Configure Docker Compose
We use two files: a base
file defining the production-like services, and an override
file that adds development conveniences like volume mounting for live code updates inside containers.
docker-compose.yml
(Base Configuration):
version: '3.8'
services:
nginx:
build:
context: .
dockerfile: nginx/Dockerfile.nginx
ports:
- "80:80"
depends_on:
- backend
backend:
build: ./backend
env_file: .env.test
environment:
-DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
depends_on:
db:
condition: service_healthy
db:
image: postgres:15-alpine
env_file: .env.test
ports:
- "5433:5432" # Map to host 5433 to avoid local conflicts
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
docker-compose.override.yml
(Development Overrides):
version: '3.8'
services:
backend:
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
volumes:
- ./backend/app:/app/app # Mount local code for hot-reloading
nginx:
ports: - "8080:80" # Use a different host port for testing
How to Run the Test Environment
Ensure the .env.test
file exists in your project root.
From the root directory, launch the entire stack:
docker-compose up --build
Docker Compose automatically merges the base and override files. You can now access your full, integrated application at http://localhost:8080
, confident it’s running in a consistent, production-like environment.
Stage 3: Confident Production Deployment
Because you’ve tested in a near-identical Docker environment, deploying is no longer a leap of faith. It’s a simple, repeatable process.
How to Prepare for Production Deployment
On your production server, your CI/CD pipeline (e.g., GitHub Actions) will automate these steps. You will need a production-ready compose file.
docker-compose.prod.yml
: This file is explicit and secure. It might add configurations for HTTPS certificates and production logging. YAMLversion: '3.8' services: nginx: ports: - "80:80" - "443:443" # Expose HTTPS port volumes: - /etc/letsencrypt:/etc/letsencrypt:ro # Mount SSL certificates backend: command: gunicorn -w 4 -k uvicorn.workers.UvicornWorker app.main:app -b 0.0.0.0:8000 # No code volumes in production! The code is baked into the image. # The db service can often be identical to the base docker-compose.yml
How to Deploy
Your CI/CD pipeline script on the production server will execute a set of simple commands:
Bash
# 1. Log in to your Docker registry (if using one)
# 2. Pull the latest code changes from your main branch
git pull
# 3. Bring down the old version and launch the new one using the prod config
# The -f flag explicitly specifies which compose files to use.
# The -d flag runs the containers in detached mode.
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
# 4. Clean up old, unused Docker images to save disk space
docker image prune -af
By following this multi-stage workflow, you create a seamless and reliable bridge from development to production, empowering you to build and deploy features faster and with greater confidence.