Skip to content
Infrastructure

From Cloud Codex to Production: GitHub, Fly.io, Neon, and Django

A practical record of taking Coding Bowl from a Cloud Codex workspace to GitHub, Fly.io, and Neon PostgreSQL.

CodingBowl··6 min read
Photo by Sandro Katalina on Unsplash

This is the path I followed to take Coding Bowl from a Cloud Codex development environment to a deployed Django site with source control, Fly.io hosting, and Neon PostgreSQL.

1. Start in Cloud Codex

I began by connecting the Django repository to a Cloud Codex environment. The environment provided a shared workspace where I could inspect the project, make changes, run checks, and work with the repository through a terminal.

The project uses uv for Python commands. Locally, the usual pattern is:

uv run --python .venv/bin/python manage.py check
uv run --python .venv/bin/python manage.py test

Using the project interpreter keeps the commands aligned with the dependencies defined for the application.

2. Commit the work to GitHub

After checking the application, I committed the changes to a work branch and pushed that branch to GitHub for review. Once the work was ready, I merged it into main.

git status
git add .
git commit -m "Describe the change"
git push origin work
git checkout main
git pull origin main
git merge work
git push origin main

GitHub became the source of truth for the deployable version of the project. Keeping commits small also made it easier to trace configuration and deployment changes.

3. Configure Fly.io

The Django project includes a fly.toml file with the Fly application name, region, HTTP service, health check, and volume mount. The application uses a mounted volume for the SQLite database when SQLite is selected, at /data/db.sqlite3.

For a new Fly application, I used commands like these:

flyctl apps create your-fly-app --org personal
flyctl volumes create data --size 1 --region sin --app your-fly-app
flyctl deploy --app your-fly-app

The Fly CLI must be installed and authenticated before these commands can work. A FLY_API_TOKEN is used by Cloud Codex or GitHub Actions when an automated deployment needs access to Fly.io.

4. Connect Neon PostgreSQL

Neon supplies the production PostgreSQL database. The connection string is kept as the DATABASE_URL environment variable rather than committed to GitHub.

The Django settings select PostgreSQL when DJANGO_DB_ENGINE=postgres and require DATABASE_URL. SQLite remains available as the local default when a local SQLite URL is used.

flyctl secrets set \\
  DJANGO_DB_ENGINE=postgres \\
  DATABASE_URL="postgresql://..." \\
  DJANGO_ALLOWED_HOSTS="your-app.fly.dev" \\
  DJANGO_CSRF_TRUSTED_ORIGINS="https://your-app.fly.dev" \\
  --app your-fly-app

The real Neon URL belongs in Fly secrets or the hosting provider's environment configuration. It should not be placed in .env.example, committed files, or article content.

5. Run Django migrations

The container startup command runs migrations after the application starts with its runtime environment available. I also used the Fly SSH console to run a migration manually when needed:

flyctl ssh console --app your-fly-app
cd /app
python manage.py migrate

The important lesson was to diagnose the database configuration before running migrations. A missing DATABASE_URL, an incorrect engine setting, or a volume path that does not exist can all make a migration appear to fail for unrelated reasons.

6. Turn on automatic deployment

The repository can deploy automatically after every push to main with a GitHub Actions workflow. The workflow should check out the repository, install flyctl, and run flyctl deploy --remote-only for the target Fly application.

Add the Fly deployment token in GitHub under Settings → Secrets and variables → Actions as a repository secret named FLY_API_TOKEN. Keep the token in GitHub Actions secrets rather than committing it to the repository or adding it to Fly runtime secrets.

With a workflow configured for the main branch, the process is:

git add .
git commit -m "Describe the change"
git push origin main

The push starts the workflow. GitHub builds the application through Fly.io's remote builder and deploys the new release. The Actions run page shows whether the deployment succeeded. A manual workflow_dispatch trigger is useful when a redeploy is needed without a new commit.

7. Verify the deployed site

After deployment, I checked the Django system configuration, migration state, health endpoint, public homepage, admin route, and article pages. I also confirmed that the production database contained the expected posts and that the deployment workflow was connected to pushes on main.

The complete workflow is straightforward once each service has one clear responsibility: Cloud Codex for development, GitHub for version control, Fly.io for the application runtime, and Neon for production data.