Deploy flask with Laravel Forge

This guide walks you through deploying a production-ready Flask application using Laravel Forge, GitHub Actions, and Supervisor — with automatic database migrations, SSH deployment, and secure environment variable management.

We’ll treat this as a fresh project with new migrations.

1.Provision Your Server

In Laravel Forge:

  1. Create a new server (Ubuntu 22+ recommended)
  2. Enable SSH access for your GitHub Actions
  3. Add a site (e.g., api.yourapp.com)
  4. Clone your Git repo (use "Deploy Script" toggle OFF for now)

On the server:

ssh forge@your-server-ip
cd /home/forge/api.yourapp.com
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

2.Project Structure & Entry Point

Your main app should have a run.py file:

from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run()

Use Flask-Migrate and SQLAlchemy in app/init.py:

from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
import os

load_dotenv()

db = SQLAlchemy()
migrate = Migrate()

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.Config')

    db.init_app(app)
    migrate.init_app(app, db)

    return app
  1. GitHub Actions Deployment

Create .github/workflows/deploy.yml:

name: Deploy to Forge

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Deploy over SSH
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.FORGE_HOST }}
        username: forge
        key: ${{ secrets.FORGE_SSH_KEY }}
        script: |
          cd /home/forge/api.yourapp.com
          git pull origin main
          source venv/bin/activate
          pip install -r requirements.txt
          flask db upgrade
          sudo supervisorctl restart flask-app

Important: Add the Forge server's SSH private key (id_rsa) to GitHub Secrets as FORGE_SSH_KEY.

  1. Managing Environment Variables

On the Forge server:

Add a .env file in your project root (/home/forge/api.yourapp.com/.env):

FLASK_ENV=production
SECRET_KEY=supersecret
DATABASE_URL=mysql+pymysql://user:pass@host:port/db
MAILGUN_DOMAIN=mg.yourapp.com
MAILGUN_API_KEY=your-mailgun-key

In app/init.py:

from dotenv import load_dotenv
load_dotenv(dotenv_path='/home/forge/api.yourapp.com/.env')

Confirm values load with:

flask shell
>>> import os
>>> os.getenv("MAILGUN_DOMAIN")
  1. Running Migrations on Deploy

When you push code with new migrations:

flask db migrate -m "Add new table"
git add migrations/
git commit -m "Add new table"
git push

GitHub Actions will:

  • Pull the latest code
  • Install dependencies
  • Run flask db upgrade live
  • Restart Gunicorn
  1. Supervisor Configuration

Set up supervisor to run your Flask app:

sudo nano /etc/supervisor/conf.d/flask-app.conf
[program:flask-app]
command=/home/forge/api.yourapp.com/venv/bin/gunicorn run:app
user=forge
directory=/home/forge/api.yourapp.com
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/flask-app.log
stderr_logfile=/var/log/supervisor/flask-app.err.log
environment=FLASK_ENV="production"

Then reload:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart flask-app

You Did It

You now have a full CI/CD pipeline:

GitHub commit → GitHub Actions → SSH to Forge → Pull, Migrate, Restart

New tables are deployed automatically

Environment is secure