Tutorial: Deploy a Flask App with Nix¶
This tutorial walks you through deploying a Flask application on Hop3 using Nix. Hop3 pins nixpkgs to a specific commit (nixos-24.11) so the toolchain is reproducible across machines and dates. By the end, you'll have a working web app where every dependency — from Python to Gunicorn — comes from that pinned nixpkgs. Because this example sources Flask and Gunicorn from nixpkgs (via python3.withPackages), those packages are pinned too; templates that instead run pip / npm or fetch upstream binaries at build time are not bit-for-bit reproducible.
Prerequisites¶
- A Hop3 server with Nix installed (the installer does this automatically)
- The Hop3 CLI installed on your local machine
- Nix installed locally for testing (
curl -L https://nixos.org/nix/install | sh)
Verify Nix is working:
Step 1: Create the Project¶
Create app.py:
import os
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return f"Hello from Nix! Running on port {os.environ.get('PORT', '?')}"
@app.route("/health")
def health():
return "ok"
Step 2: Write the Nix Expression¶
Create hop3.nix:
{ pkgs ? import (fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/50ab793786d9de88ee30ec4e4c24fb4236fc2674.tar.gz";
sha256 = "1s2gr5rcyqvpr58vxdcb095mdhblij9bfzaximrva2243aal3dgx";
}) {} }:
let
# Define our Python environment with exact packages, drawn from the pinned nixpkgs above
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
flask
gunicorn
]);
# Build the application package
app = pkgs.stdenv.mkDerivation {
pname = "nix-flask-demo";
version = "0.1.0";
src = ./.;
buildInputs = [ pythonEnv ];
# Pure Python app — no compilation needed
dontBuild = true;
installPhase = ''
# Create directories
mkdir -p $out/app $out/bin $out/hop3
# Copy application code
cp app.py $out/app/
# Create a wrapper script
cat > $out/bin/start << 'WRAPPER'
#!/bin/sh
exec ${pythonEnv}/bin/python -m gunicorn app:app "$@"
WRAPPER
chmod +x $out/bin/start
# Runtime configuration for Hop3
cat > $out/hop3/runtime.json << EOF
{
"workers": {
"web": "$out/bin/start --bind \$BIND_ADDRESS:\$PORT --chdir $out/app"
},
"env": {
"FLASK_ENV": "production",
"PYTHONDONTWRITEBYTECODE": "1"
},
"path": [
"$out/bin",
"${pythonEnv}/bin"
]
}
EOF
'';
};
in {
package = app;
}
What this does:
pythonEnvcreates a Python environment with Flask and Gunicorn from nixpkgs- The derivation copies
app.pyto the Nix store and creates a wrapper script runtime.jsontells Hop3 how to start the app: run Gunicorn on$BIND_ADDRESS:$PORT
Step 3: Configure Hop3¶
Create hop3.toml:
That's it. Hop3 reads hop3.nix and handles the rest.
Step 4: Test the Build Locally¶
You should see output like:
Inspect the output:
result=$(nix-build hop3.nix -A package --no-out-link)
cat "$result/hop3/runtime.json"
ls "$result/bin/"
ls "$result/app/"
Optionally, test it runs:
Visit http://localhost:5000 — you should see "Hello from Nix!".
Step 5: Deploy to Hop3¶
Or via git push:
git init && git add -A && git commit -m "Initial commit"
git remote add hop3 ssh://hop3@your-server/home/hop3/repos/nix-flask-demo.git
git push hop3 main
Hop3 will:
- Detect
hop3.nixand select the NixBuilder - Run
nix-buildon the server - Read
runtime.jsonfrom the build output - Start Gunicorn as a managed daemon
- Configure nginx to proxy requests to the app
Step 6: Verify¶
Adding a Database¶
To add PostgreSQL, update hop3.toml:
Hop3 provisions the database and injects DATABASE_URL, PGHOST, PGPORT, etc. into your app's environment. Use them in your Flask app:
What's Different from Native Deployment?¶
| Aspect | Native (buildpack) | Nix |
|---|---|---|
| Dependencies | requirements.txt |
hop3.nix (nixpkgs) |
| Build tool | pip in virtualenv | nix-build |
| Reproducibility | Depends on PyPI state | Reproducible (toolchain pinned via nixpkgs) |
| Build speed | Fast (pip cache) | Slower first build, cached after |
| Configuration | Automatic detection | Explicit hop3.nix |
Next Steps¶
- See Deploying with Nix for patterns in Go, Node.js, Ruby, and static sites
- See Nix Integration Reference for the full
runtime.jsonspecification - Browse working examples in
apps/nix-apps/(10 apps across 6 languages)