Thursday, April 17, 2025

 

Step by Step Guide: Deploying a FastAPI App to Google Cloud Run with a Custom Namecheap Domain


This walkthrough covers every step—from local setup to multi‑arch Docker builds on Apple Silicon, to pushing updates, to wiring up your Namecheap domain—so you can host your FastAPI app in Cloud Run under https://yourdomain.com.


Part 1: Initial Cloud Run Deployment


1. Install & Authenticate the Google Cloud SDK

  1. Download & install the Google Cloud SDK.

  2. Open a new terminal and run:

gcloud init

    • Sign in when prompted.

    • Select or create your GCP project.

  1. Enable required services:

gcloud services enable \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  containerregistry.googleapis.com



2. Prepare Your FastAPI Code & Dockerfile


Your repo layout:

/concept_explorer
├─ backend/           ← FastAPI app + requirements.txt
│   └─ main.py
├─ frontend/          ← static assets (index.html, script.js, etc.)
└─ Dockerfile

Use this multi‑stage Dockerfile (listening on $PORT):

# syntax=docker/dockerfile:1

# 1) Build stage
FROM python:3.11-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y build-essential \
 && rm -rf /var/lib/apt/lists/*

COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 2) Final image
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY backend/ ./backend
COPY frontend/ ./frontend
EXPOSE 8080
CMD ["sh","-c","uvicorn backend.main:app --host 0.0.0.0 --port ${PORT:-8080}"]

3. Build & Push the Docker Image


a) On Intel / Linux hosts

export PROJECT_ID=your‑gcp‑project
export IMAGE_NAME=concept-explorer

docker build -t gcr.io/$PROJECT_ID/$IMAGE_NAME:latest .
docker push    gcr.io/$PROJECT_ID/$IMAGE_NAME:latest

b) On Apple Silicon (M1/M2) — Multi‑Arch Build

  1. Create and use a Buildx builder:

docker buildx create --name multiarch --use
docker buildx inspect --bootstrap


  1. Build & push for both amd64 and arm64:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag gcr.io/$PROJECT_ID/$IMAGE_NAME:latest \
  --push \
  .



4. Deploy to Cloud Run

gcloud run deploy $IMAGE_NAME \
  --image gcr.io/$PROJECT_ID/$IMAGE_NAME:latest \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

You’ll see:

Service URL: https://concept-explorer-<hash>.run.app



Part 2: Redeploying After Code Changes


Whenever you update your FastAPI or frontend code:

  1. Rebuild & push (using the same multi‑arch steps if on Apple Silicon):

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag gcr.io/$PROJECT_ID/$IMAGE_NAME:latest \
  --push \
  .


  1. Redeploy:

gcloud run deploy $IMAGE_NAME \
  --image gcr.io/$PROJECT_ID/$IMAGE_NAME:latest \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated



Your Cloud Run service will automatically roll out the new revision.


Part 3: Mapping & Securing Your Namecheap Domain


1. Verify Domain Ownership in Google Search Console

  1. Go to Google Search Console.

  2. Add PropertyDomainyourdomain.com → follow DNS TXT verification instructions.

  3. Wait until Search Console shows Verified.


2. Create a Cloud Run Domain Mapping

gcloud beta run domain-mappings create \
  --service concept-explorer \
  --domain conceptexplorer.space \
  --platform managed \
  --region us-central1

Output will list the A and AAAA records you must add:

NAME                TYPE   CONTENTS
@                   A      206.289.32.91
@                   A      206.289.34.91
@                   A      206.289.36.91
@                   A      206.289.38.91
@                   AAAA   2001:4860:4812:52::15
@                   AAAA   2001:4860:4812:54::15
@                   AAAA   2001:4860:4812:56::15
@                   AAAA   2001:4860:4812:58::15

3. Configure Namecheap DNS


In your Namecheap domain’s Advanced DNS panel:

Type

Host

Value

TTL

A

@

206.289.32.91

Automatic

A

@

206.289.34.91

Automatic

A

@

206.289.36.91

Automatic

A

@

206.289.38.91

Automatic

AAAA

@

2001:4860:4812:52::15

Automatic

AAAA

@

2001:4860:4812:54::15

Automatic

AAAA

@

2001:4860:4812:56::15

Automatic

AAAA

@

2001:4860:4812:58::15

Automatic

Note: Host “@” means the root/apex domain.


4. Wait for Certificate Provisioning

  • Cloud Run provisions a Let’s Encrypt certificate automatically.

  • Propagation time: anywhere from 5 min up to 1 hr.

  • Check status in Cloud Console → Cloud Run → Domain Mappings; the STATUS column should turn READY.


5. Verify Everything

# Confirm DNS
dig +short A    conceptexplorer.space
dig +short AAAA conceptexplorer.space

# Confirm mapping status
gcloud beta run domain-mappings list \
  --platform managed \
  --region us-central1

When you see:

DOMAIN                 SERVICE           REGION      STATUS
conceptexplorer.space  concept-explorer  us-central1 READY

you can browse to https://conceptexplorer.space and your app will load securely.


Quick‑Reference Command Summary

# 1. Authenticate & select project
gcloud init
gcloud services enable run.googleapis.com cloudbuild.googleapis.com containerregistry.googleapis.com

# 2. Build & push image (Apple Silicon => multiarch)
docker buildx create --name multiarch --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag gcr.io/$PROJECT_ID/concept-explorer:latest \
  --push \
  .

# 3. Deploy to Cloud Run
gcloud run deploy concept-explorer \
  --image gcr.io/$PROJECT_ID/concept-explorer:latest \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

# 4. Map custom domain (after Search Console verification)
gcloud beta run domain-mappings create \
  --service concept-explorer \
  --domain conceptexplorer.space \
  --platform managed \
  --region us-central1

# 5. Namecheap DNS: add the A & AAAA records provided by step 4.

# 6. Redeploy after code changes:
docker buildx build … --push …
gcloud run deploy …

Follow these steps in order and you’ll have your FastAPI app running on Cloud Run, reachable at https://yourdomain.com, with seamless updates and secure HTTPS.

Wednesday, April 16, 2025

 

Deploying Your FastAPI + Simple Frontend App on Fly.io


This guide walks you through every step—from local setup to a live endpoint on Fly.io—using the “Concept Explorer” example. You’ll learn how to:

  1. Prepare your code & GitHub repo

  2. Write your Dockerfile

  3. Create & configure fly.toml

  4. Set up Fly.io secrets

  5. Connect your GitHub repo to Fly.io (optional CI)

  6. Deploy & monitor

  7. Test your live app


1. Prepare Your Code & GitHub Repo

  1. Project Layout

concept_explorer/
├── backend/
│   ├── main.py
│   ├── explorer.py
│   ├── models.py
│   └── requirements.txt
├── frontend/
│   └── index.html
├── Dockerfile
├── fly.toml
└── .gitignore


  1. Create .gitignore at the repo root:

# Python & env
__pycache__/
*.py[cod]
.env
venv/
backend/*.pyc

# Docker
.dockerignore

# IDE
.idea/
.vscode/


  1. Initialize Git & push to GitHub

cd concept_explorer
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/your‑username/concept_explorer.git
git push -u origin main



2. Write Your 

Dockerfile


Place this at the project root as Dockerfile:

# syntax=docker/dockerfile:1

############################
# 1. Build stage
############################
FROM python:3.11-slim AS builder
WORKDIR /app
RUN apt-get update \
 && apt-get install -y --no-install-recommends build-essential \
 && rm -rf /var/lib/apt/lists/*
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

############################
# 2. Final image
############################
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# Copy application code
COPY backend/ ./backend
COPY frontend/ ./frontend

EXPOSE 8000
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
Tip: You can test locally with:
docker build -t concept-explorer .
docker run -p 8000:8000 concept-explorer
# then visit http://localhost:8000



3. Create & Configure 

fly.toml


A) Generate via CLI


If you have flyctl installed:

flyctl launch --name concept-explorer --region den --dockerfile Dockerfile --no-deploy

That creates fly.toml.


B) Manual 

fly.toml


Otherwise, create fly.toml at root:

app = "concept-explorer"
primary_region = "den"

[build]
  dockerfile = "Dockerfile"

[env]
  # non-sensitive defaults, if any
  LOG_LEVEL = "info"

[http_service]
  internal_port = 8000
  force_https = true
  auto_start_machines = true
  auto_stop_machines = true

  • app must match your Fly app’s name.

  • internal_port = the port your Uvicorn server listens on (8000).


4. Set Up Fly.io Secrets


Your .env holds keys locally, but in production you must inject them as Fly Secrets:

  1. Via CLI

flyctl secrets set GEMINI_API_KEY=your‑gemini‑key OPENAI_API_KEY=your‑openai‑key -a concept-explorer


  1. Via Dashboard

    • Visit your app → SecretsNew Secret.

    • Add GEMINI_API_KEY and/or OPENAI_API_KEY.


Secrets become environment variables at runtime, so your backend/main.py—which calls load_dotenv()—will pick them up via os.getenv(...).


5. Connect GitHub → Fly.io (optional CI)

  1. In the Fly Dashboard, go to DeploymentsConnect repository.

  2. Authorize your GitHub org & pick the concept_explorer repo + main branch.

  3. Enable “Auto Deploy on push”.


Now every git push to main triggers Fly’s build+deploy pipeline automatically.


6. Deploy & Monitor


A) First Deploy (or manual)


If you didn’t enable auto-CI, run:

flyctl deploy -a concept-explorer

Fly will:

  • Build your Docker image using Dockerfile.

  • Push it to Fly’s registry.

  • Launch (or restart) a VM running your container.

  • Wire up your HTTP service on port 80/443 → container’s port 8000.


B) Watch Live Logs

flyctl logs -a concept-explorer --tail

Or in the Dashboard under Live Logs. Look for:

  • Serving static files from: ...frontend

  • Uvicorn running on 0.0.0.0:8000

  • No ModuleNotFoundError or missing‐key errors.


7. Test Your Live App

  1. Frontend

    Visit:

https://concept-explorer.fly.dev/


  1. Stream Endpoint

    Test SSE:

curl "https://concept-explorer.fly.dev/stream_exploration?root_concept=vedanta&provider=gemini&depth=2"


  1. Health Check

curl https://concept-explorer.fly.dev/health
# should return {"status":"ok"}



Troubleshooting

  • ModuleNotFoundError:

    Make sure your imports use relative paths (e.g. from .explorer import ConceptExplorer) and that backend/ is copied in your Dockerfile.

  • Secrets not applied:

    • Verify fly secrets list -a concept-explorer.

    • Redeploy to inject new secrets: flyctl deploy -a concept-explorer.

  • No build on push:

    • Confirm GitHub integration under Deployments → “Connected to GitHub”.

    • Ensure .github/workflows/fly.yml exists if you use Actions, or rely on Fly’s GitHub App.

  • Port binding errors:

    • Your app must listen on 0.0.0.0:8000 (not 127.0.0.1).

    • fly.toml[http_service] internal_port = 8000.



Part 2: Custom Domain on Fly.io

 

 


1. Deploy your app on Fly.io


Make sure your app is running on Fly.io and that you’ve confirmed its public IPv4 and IPv6 addresses:

flyctl ips list --app <YOUR_APP_NAME>

You’ll see something like:

VERSION   IP                  TYPE
v4        <YOUR_IPV4_ADDRESS>   public (shared)
v6        <YOUR_IPV6_ADDRESS>   public (dedicated)



2. Add DNS records in Namecheap

  1. Log in to Namecheap and go to Domain ListManage next to your domain.

  2. Click the Advanced DNS tab.

  3. Under Host Records, add two records:

Type

Host

Value

TTL

A

@

<YOUR_IPV4_ADDRESS>

Automatic

AAAA

@

<YOUR_IPV6_ADDRESS>

Automatic


  1. (Optional) To support www, add a CNAME:

Type

Host

Value

TTL

CNAME

www

<YOUR_FLY_APP_NAME>.fly.dev

Automatic


  1. Save your changes.


3. Request a TLS certificate on Fly.io


Fly.io will use Let’s Encrypt to issue a certificate once it can see your DNS changes.

flyctl certs create <YOUR_DOMAIN> --app <YOUR_APP_NAME>

You should see instructions if Fly still needs you to add an AAAA or A record—but since you’ve already pointed both, it will proceed automatically.


4. Verify issuance


Every few minutes, check the certificate status:

flyctl certs list --app <YOUR_APP_NAME>

You’ll see something like:

Host Name        Added           Status
<YOUR_DOMAIN>    2 minutes ago   OK (expires 2025‑XX‑XX)

When Status shows OK, your TLS cert is live.


5. Test your custom domain


Open in your browser or via curl:

curl -I https://<YOUR_DOMAIN>

You should get back a 200 OK (or the headers from your app) and a valid Let’s Encrypt certificate chain.


Tips & Troubleshooting

  • DNS propagation can take 5–15 minutes.

  • Ensure there are no conflicting records (e.g., an extra CNAME on @).

  • If, after 20 minutes, the certificate is still Awaiting configuration, you can remove & re‑create it:

flyctl certs remove <YOUR_DOMAIN> --app <YOUR_APP_NAME>
flyctl certs create <YOUR_DOMAIN> --app <YOUR_APP_NAME>


  • For a www‑only site, you can point www→Fly.io via CNAME and then redirect the root (@) to www using Namecheap’s URL Redirect feature.


That’s it! Your Namecheap domain will now serve your Fly.io app over HTTPS.