Project Structure Reference¶
Recommended project organization for Airbase applications
This document provides the reference for organizing your project files, configuration, and code for deployment to Airbase.
Overview¶
A well-structured project makes development, deployment, and maintenance easier. This reference covers standard patterns for different application types.
Key concepts: - Required files: Minimum files needed for deployment - Recommended structure: Best practices for organization - Framework conventions: Standard patterns per framework - Configuration location: Where to place config files
Required Files¶
Every Airbase project must include these files:
1. airbase.json¶
Location: Project root
Purpose: Airbase configuration
Required fields:
{
"framework": "container",
"handle": "namespace/project-name",
"port": 3000,
"instanceType": "b.small"
}
See: airbase.json Configuration for complete reference
2. Dockerfile¶
Location: Project root
Purpose: Container build instructions
Minimum requirements: - Use Airbase base image - Set USER app - Set appropriate file ownership (chown app:app) - Bind to $PORT environment variable - Expose port 3000
Example:
FROM gdssingapore/airbase:node-22
WORKDIR /app
COPY --chown=app:app . ./
USER app
EXPOSE 3000
CMD ["node", "index.js"]
See: Dockerfile Requirements for complete reference
3. .dockerignore¶
Location: Project root
Purpose: Exclude files from Docker build context
Recommended content:
Recommended Files¶
.env Files¶
Location: Project root
Purpose: Environment-specific configuration
Recommended files:
.env # Production configuration
.env.staging # Staging configuration
.env.development # Local development (gitignored)
.env.example # Template (committed to git)
See: Environment Variables Reference
.gitignore¶
Location: Project root
Purpose: Exclude files from version control
Recommended content:
# Dependencies
node_modules/
venv/
__pycache__/
# Environment variables
.env
.env.local
.env.development
.env.*.local
# Build outputs
dist/
build/
site/
*.pyc
# Logs
*.log
logs/
# OS files
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
README.md¶
Location: Project root
Purpose: Project documentation
Recommended sections:
# Project Name
## Description
Brief description of the application
## Prerequisites
- Node.js 22 / Python 3.13
- Docker
## Local Development
```bash
npm install
npm run dev
Deployment¶
Environment Variables¶
See .env.example for required configuration
my-express-app/ ├── airbase.json # Airbase configuration ├── Dockerfile # Container build ├── .dockerignore # Docker exclusions ├── .env # Production config ├── .env.staging # Staging config ├── .env.example # Config template ├── .gitignore # Git exclusions ├── package.json # Dependencies ├── package-lock.json # Locked dependencies ├── README.md # Documentation ├── src/ # Source code │ ├── index.js # Entry point │ ├── app.js # Express app │ ├── routes/ # Route handlers │ │ ├── index.js │ │ ├── users.js │ │ └── health.js │ ├── controllers/ # Business logic │ │ └── userController.js │ ├── models/ # Data models │ │ └── user.js │ ├── middleware/ # Express middleware │ │ └── auth.js │ ├── config/ # Configuration │ │ └── database.js │ └── utils/ # Utilities │ └── logger.js └── tests/ # Test files └── app.test.js**Key conventions:**
- Entry point: `src/index.js` or `index.js`
- Port: Read from `process.env.PORT`
- Health check: `/health` endpoint
- Separate routes, controllers, models
**Example index.js:**
```javascript
const app = require('./app');
const PORT = process.env.PORT || 3000;
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
Python/Flask Application¶
my-flask-app/
├── airbase.json # Airbase configuration
├── Dockerfile # Container build
├── .dockerignore # Docker exclusions
├── .env # Production config
├── .env.staging # Staging config
├── .env.example # Config template
├── .gitignore # Git exclusions
├── requirements.txt # Dependencies
├── README.md # Documentation
├── app.py # Entry point (or wsgi.py)
├── config.py # Configuration
├── app/ # Application package
│ ├── __init__.py # App factory
│ ├── routes/ # Route blueprints
│ │ ├── __init__.py
│ │ ├── main.py
│ │ └── api.py
│ ├── models/ # Data models
│ │ ├── __init__.py
│ │ └── user.py
│ ├── services/ # Business logic
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── utils/ # Utilities
│ ├── __init__.py
│ └── logger.py
└── tests/ # Test files
└── test_app.py
Key conventions: - Entry point: app.py or wsgi.py - Port: Read from os.environ.get('PORT') - Health check: /health endpoint - Use blueprints for routes - Package structure with __init__.py
Example app.py:
import os
from app import create_app
app = create_app()
PORT = int(os.environ.get('PORT', 3000))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=PORT)
Python/Streamlit Application¶
my-streamlit-app/
├── airbase.json # Airbase configuration
├── Dockerfile # Container build
├── .dockerignore # Docker exclusions
├── .env # Production config
├── .env.staging # Staging config
├── .env.example # Config template
├── .gitignore # Git exclusions
├── requirements.txt # Dependencies
├── README.md # Documentation
├── main.py # Streamlit entry point
├── config.py # Configuration
├── pages/ # Multi-page app pages
│ ├── 1_📊_Dashboard.py
│ ├── 2_📈_Analytics.py
│ └── 3_⚙️_Settings.py
├── utils/ # Utility functions
│ ├── __init__.py
│ ├── data_loader.py
│ └── visualizations.py
├── data/ # Static data files
│ └── sample.csv
└── tests/ # Test files
└── test_utils.py
Key conventions: - Entry point: main.py or app.py - Multi-page apps: pages/ directory with numbered files - Utilities in utils/ directory - Static data in data/ directory
Example main.py:
import streamlit as st
import os
# Configuration
st.set_page_config(
page_title="My Dashboard",
page_icon="📊",
layout="wide"
)
# Main app
st.title("My Streamlit Dashboard")
st.write("Welcome to my application")
Dockerfile CMD:
Static Site (React/Vite)¶
my-react-app/
├── airbase.json # Airbase configuration
├── Dockerfile # Multi-stage build
├── .dockerignore # Docker exclusions
├── .gitignore # Git exclusions
├── package.json # Dependencies
├── package-lock.json # Locked dependencies
├── README.md # Documentation
├── nginx.conf # Nginx configuration
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript configuration
├── index.html # HTML entry point
├── public/ # Static assets
│ ├── favicon.ico
│ └── robots.txt
└── src/ # Source code
├── main.tsx # Entry point
├── App.tsx # Root component
├── components/ # React components
│ ├── Header.tsx
│ └── Footer.tsx
├── pages/ # Page components
│ ├── Home.tsx
│ └── About.tsx
├── styles/ # CSS/SCSS
│ └── main.css
├── utils/ # Utilities
│ └── api.ts
└── assets/ # Images, fonts
└── logo.svg
Key conventions: - Multi-stage Dockerfile (builder + Nginx) - Build output served by Nginx - Client-side routing with try_files - CSP-compliant Vite configuration
nginx.conf:
server {
listen 3000;
root /usr/share/nginx/html;
index index.html;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Configuration File Locations¶
airbase.json¶
Location: Project root
Contents:
{
"framework": "container",
"handle": "namespace/project-name",
"port": 3000,
"instanceType": "b.small"
}
Environment Variables¶
Location: Project root
Files: - .env - Production - .env.staging - Staging - .env.development - Local development - .env.example - Template (commit this)
Never commit: .env, .env.staging, .env.development
Dockerfile¶
Location: Project root
Name: Must be exactly Dockerfile (capital D)
Package Dependencies¶
Node.js: - package.json - Project root - package-lock.json - Project root (commit this)
Python: - requirements.txt - Project root
Directory Naming Conventions¶
Source Code¶
Node.js: - src/ - Main source code - lib/ - Alternative to src/ - dist/ or build/ - Build output (gitignore these)
Python: - app/ - Application package - src/ - Alternative source directory - __pycache__/ - Python cache (gitignore this)
Static Sites: - src/ - Source code - public/ - Static assets - dist/ or build/ - Build output (gitignore this)
Tests¶
Common patterns: - tests/ - All test files - test/ - Alternative - __tests__/ - Jest convention (Node.js) - *.test.js or *.spec.js - Test files
Configuration¶
Common patterns: - config/ - Configuration files - .env files - Project root - *.config.js - Build tool configs (root)
Best Practices¶
✅ Do's¶
1. Keep configuration at root:
✅ Good:
my-app/
├── airbase.json
├── Dockerfile
├── .env
└── src/
❌ Bad:
my-app/
└── config/
├── airbase.json
├── Dockerfile
└── .env
2. Use standard directory names: - src/ for source code - tests/ for tests - public/ for static assets
3. Separate concerns:
src/
├── routes/ # HTTP routes
├── controllers/ # Business logic
├── models/ # Data models
├── utils/ # Utilities
└── config/ # Configuration
4. Use .dockerignore:
5. Provide .env.example:
# .env.example (commit this)
DATABASE_URL=postgres://localhost:5432/myapp
API_KEY=your-api-key-here
LOG_LEVEL=info
❌ Don'ts¶
1. Don't commit secrets:
❌ Bad - committed .env:
.env # Contains production secrets
✅ Good - gitignored .env:
.env.example # Template only
2. Don't nest configuration too deep:
❌ Bad:
my-app/
└── config/
└── deployment/
└── airbase/
└── airbase.json
✅ Good:
my-app/
└── airbase.json
3. Don't mix source and build output:
❌ Bad:
src/
├── app.js
└── dist/
└── app.bundle.js
✅ Good:
src/
└── app.js
dist/ # Separate top-level
└── app.bundle.js
4. Don't use inconsistent naming:
❌ Bad:
my-app/
├── Sources/
├── Tests/
├── Utils/
└── config/
✅ Good (consistent lowercase):
my-app/
├── src/
├── tests/
├── utils/
└── config/
Examples¶
Minimal Node.js API¶
minimal-api/
├── airbase.json
├── Dockerfile
├── .dockerignore
├── .env.example
├── .gitignore
├── package.json
├── package-lock.json
└── index.js
Use case: Simple REST API with no complex structure
Standard Python Web App¶
standard-flask-app/
├── airbase.json
├── Dockerfile
├── .dockerignore
├── .env.example
├── .gitignore
├── requirements.txt
├── app.py
├── config.py
├── app/
│ ├── __init__.py
│ ├── routes/
│ ├── models/
│ └── utils/
└── tests/
Use case: Flask application with blueprints
Production React SPA¶
production-react-spa/
├── airbase.json
├── Dockerfile
├── .dockerignore
├── .gitignore
├── nginx.conf
├── package.json
├── vite.config.ts
├── tsconfig.json
├── index.html
├── public/
└── src/
├── main.tsx
├── App.tsx
├── components/
├── pages/
├── utils/
└── styles/
Use case: Production-ready React SPA with TypeScript
Migration from Other Platforms¶
From Heroku¶
Changes needed: 1. Add airbase.json 2. Update Dockerfile to use Airbase base images 3. Change port binding from process.env.PORT || 3000 to process.env.PORT (required) 4. Add health check endpoint at /health 5. Ensure USER app in Dockerfile
Procfile not used - Use Dockerfile CMD instead
From Vercel/Netlify¶
Changes needed: 1. Add airbase.json 2. Create multi-stage Dockerfile (builder + Nginx) 3. Add nginx.conf for serving 4. Configure SPA routing with try_files 5. Ensure CSP-compliant build (external scripts only)
Serverless functions not supported - Use backend API instead
From Docker Compose¶
Changes needed: 1. Add airbase.json 2. Use Airbase base images 3. Update Dockerfile with USER app 4. Use .env files instead of docker-compose.yml environment 5. Database/Redis must be external services (not in compose)
Multi-container not supported - Deploy each service separately
Troubleshooting¶
"File not found" during build¶
Problem: Dockerfile cannot find source files
Solution: 1. Check .dockerignore is not excluding source files 2. Verify COPY paths are correct 3. Ensure files exist in repository
"Permission denied" errors¶
Problem: Files not accessible to app user
Solution:
Environment variables not loading¶
Problem: .env file not found
Solution: 1. Ensure .env or .env.staging exists in project root 2. Check file names match environment (.env.staging for staging) 3. Verify .env not in .gitignore (it should be, use deployment-specific .env)
See Also¶
- Reference: Dockerfile Requirements - Dockerfile specification
- Reference: airbase.json Configuration - Configuration reference
- Reference: Environment Variables - Environment configuration
- How-To: Build and Deploy - Deployment process