13 KiB
Coolify Database Architecture & Patterns
Database Strategy
Coolify uses PostgreSQL 15 as the primary database with Redis 7 for caching and real-time features. The architecture supports managing multiple external databases across different servers.
Primary Database (PostgreSQL)
Core Tables & Models
User & Team Management
- User.php - User authentication and profiles
- Team.php - Multi-tenant organization structure
- TeamInvitation.php - Team collaboration invitations
- PersonalAccessToken.php - API token management
Infrastructure Management
- Server.php - Physical/virtual server definitions (46KB, complex)
- PrivateKey.php - SSH key management
- ServerSetting.php - Server-specific configurations
Project Organization
- Project.php - Project containers for applications
- Environment.php - Environment isolation (staging, production, etc.)
- ProjectSetting.php - Project-specific settings
Application Deployment
- Application.php - Main application entity (74KB, highly complex)
- ApplicationSetting.php - Application configurations
- ApplicationDeploymentQueue.php - Deployment orchestration
- ApplicationPreview.php - Preview environment management
Service Management
- Service.php - Service definitions (58KB, complex)
- ServiceApplication.php - Service components
- ServiceDatabase.php - Service-attached databases
Database Type Support
Standalone Database Models
Each database type has its own dedicated model with specific configurations:
SQL Databases
- StandalonePostgresql.php - PostgreSQL instances
- StandaloneMysql.php - MySQL instances
- StandaloneMariadb.php - MariaDB instances
NoSQL & Analytics
- StandaloneMongodb.php - MongoDB instances
- StandaloneClickhouse.php - ClickHouse analytics
Caching & In-Memory
- StandaloneRedis.php - Redis instances
- StandaloneKeydb.php - KeyDB instances
- StandaloneDragonfly.php - Dragonfly instances
Configuration Management
Environment Variables
- EnvironmentVariable.php - Application-specific environment variables
- SharedEnvironmentVariable.php - Shared across applications
Settings Hierarchy
- InstanceSettings.php - Global Coolify instance settings
- ServerSetting.php - Server-specific settings
- ProjectSetting.php - Project-level settings
- ApplicationSetting.php - Application settings
Storage & Backup Systems
Storage Management
- S3Storage.php - S3-compatible storage configurations
- LocalFileVolume.php - Local filesystem volumes
- LocalPersistentVolume.php - Persistent volume management
Backup Infrastructure
- ScheduledDatabaseBackup.php - Automated backup scheduling
- ScheduledDatabaseBackupExecution.php - Backup execution tracking
Task Scheduling
- ScheduledTask.php - Cron job management
- ScheduledTaskExecution.php - Task execution history
Notification & Integration Models
Notification Channels
- EmailNotificationSettings.php - Email notifications
- DiscordNotificationSettings.php - Discord integration
- SlackNotificationSettings.php - Slack integration
- TelegramNotificationSettings.php - Telegram bot
- PushoverNotificationSettings.php - Pushover notifications
Source Control Integration
- GithubApp.php - GitHub App integration
- GitlabApp.php - GitLab integration
OAuth & Authentication
- OauthSetting.php - OAuth provider configurations
Docker & Container Management
Container Orchestration
- StandaloneDocker.php - Standalone Docker containers
- SwarmDocker.php - Docker Swarm management
SSL & Security
- SslCertificate.php - SSL certificate management
Database Migration Strategy
Migration Location: database/migrations/
Migration Patterns
// Typical Coolify migration structure
Schema::create('applications', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('fqdn')->nullable();
$table->json('environment_variables')->nullable();
$table->foreignId('destination_id');
$table->foreignId('source_id');
$table->timestamps();
});
Schema Versioning
- Incremental migrations for database evolution
- Data migrations for complex transformations
- Rollback support for deployment safety
Eloquent Model Patterns
Base Model Structure
- BaseModel.php - Common model functionality
- UUID primary keys for distributed systems
- Soft deletes for audit trails
- Activity logging with Spatie package
CRITICAL: Mass Assignment Protection
When adding new database columns, you MUST update the model's $fillable array. Without this, Laravel will silently ignore mass assignment operations like Model::create() or $model->update().
Checklist for new columns:
- ✅ Create migration file
- ✅ Run migration
- ✅ Add column to model's
$fillablearray - ✅ Update any Livewire components that sync this property
- ✅ Test that the column can be read and written
Example:
class Server extends BaseModel
{
protected $fillable = [
'name',
'ip',
'port',
'is_validating', // ← MUST add new columns here
];
}
Relationship Patterns
// Typical relationship structure in Application model
class Application extends Model
{
public function server()
{
return $this->belongsTo(Server::class);
}
public function environment()
{
return $this->belongsTo(Environment::class);
}
public function deployments()
{
return $this->hasMany(ApplicationDeploymentQueue::class);
}
public function environmentVariables()
{
return $this->hasMany(EnvironmentVariable::class);
}
}
Model Traits
// Common traits used across models
use SoftDeletes;
use LogsActivity;
use HasFactory;
use HasUuids;
Caching Strategy (Redis)
Cache Usage Patterns
- Session storage - User authentication sessions
- Queue backend - Background job processing
- Model caching - Expensive query results
- Real-time data - WebSocket state management
Cache Keys Structure
coolify:session:{session_id}
coolify:server:{server_id}:status
coolify:deployment:{deployment_id}:logs
coolify:user:{user_id}:teams
Query Optimization Patterns
Eager Loading
// Optimized queries with relationships
$applications = Application::with([
'server',
'environment.project',
'environmentVariables',
'deployments' => function ($query) {
$query->latest()->limit(5);
}
])->get();
Chunking for Large Datasets
// Processing large datasets efficiently
Server::chunk(100, function ($servers) {
foreach ($servers as $server) {
// Process server monitoring
}
});
Database Indexes
- Primary keys on all tables
- Foreign key indexes for relationships
- Composite indexes for common queries
- Unique constraints for business rules
Request-Level Caching with ownedByCurrentTeamCached()
Many models have both ownedByCurrentTeam() (returns query builder) and ownedByCurrentTeamCached() (returns cached collection). Always prefer the cached version to avoid duplicate database queries within the same request.
Models with cached methods available:
Server,PrivateKey,ProjectApplicationStandalonePostgresql,StandaloneMysql,StandaloneRedis,StandaloneMariadb,StandaloneMongodb,StandaloneKeydb,StandaloneDragonfly,StandaloneClickhouseService,ServiceApplication,ServiceDatabase
Usage patterns:
// ✅ CORRECT - Uses request-level cache (via Laravel's once() helper)
$servers = Server::ownedByCurrentTeamCached();
// ❌ AVOID - Makes a new database query each time
$servers = Server::ownedByCurrentTeam()->get();
// ✅ CORRECT - Filter cached collection in memory
$activeServers = Server::ownedByCurrentTeamCached()->where('is_active', true);
$server = Server::ownedByCurrentTeamCached()->firstWhere('id', $serverId);
$serverIds = Server::ownedByCurrentTeamCached()->pluck('id');
// ❌ AVOID - Making filtered database queries when data is already cached
$activeServers = Server::ownedByCurrentTeam()->where('is_active', true)->get();
When to use which:
ownedByCurrentTeamCached()- Default choice for reading team dataownedByCurrentTeam()- Only when you need to chain query builder methods that can't be done on collections (likewith()for eager loading), or when you explicitly need a fresh database query
Implementation pattern for new models:
/**
* Get query builder for resources owned by current team.
* If you need all resources without further query chaining, use ownedByCurrentTeamCached() instead.
*/
public static function ownedByCurrentTeam()
{
return self::whereTeamId(currentTeam()->id);
}
/**
* Get all resources owned by current team (cached for request duration).
*/
public static function ownedByCurrentTeamCached()
{
return once(function () {
return self::ownedByCurrentTeam()->get();
});
}
Data Consistency Patterns
Database Transactions
// Atomic operations for deployment
DB::transaction(function () {
$application = Application::create($data);
$application->environmentVariables()->createMany($envVars);
$application->deployments()->create(['status' => 'queued']);
});
Model Events
// Automatic cleanup on model deletion
class Application extends Model
{
protected static function booted()
{
static::deleting(function ($application) {
$application->environmentVariables()->delete();
$application->deployments()->delete();
});
}
}
Backup & Recovery
Database Backup Strategy
- Automated PostgreSQL backups via scheduled tasks
- Point-in-time recovery capability
- Cross-region backup replication
- Backup verification and testing
Data Export/Import
- Application configurations export/import
- Environment variable bulk operations
- Server configurations backup and restore
Performance Monitoring
Query Performance
- Laravel Telescope for development debugging
- Slow query logging in production
- Database connection pooling
- Read replica support for scaling
Metrics Collection
- Database size monitoring
- Connection count tracking
- Query execution time analysis
- Cache hit rates monitoring
Multi-Tenancy Pattern
Team-Based Isolation
// Global scope for team-based filtering
class Application extends Model
{
protected static function booted()
{
static::addGlobalScope('team', function (Builder $builder) {
if (auth()->user()) {
$builder->whereHas('environment.project', function ($query) {
$query->where('team_id', auth()->user()->currentTeam->id);
});
}
});
}
}
Data Separation
- Team-scoped queries by default
- Cross-team access controls
- Admin access patterns
- Data isolation guarantees