railway-postgres-manager
AI-powered 3D asset generation platform for game development
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- GitHub stars: 2
- Updated: 2025-11-21
Download the latest version (v1.0.0) from this page.
SKILL.md
# Railway Postgres Manager
## Activation
When the user mentions any of these, activate this skill:
- "railway postgres"
- "railway database"
- "postgres on railway"
- "database migration railway"
- "railway connect postgres"
- "postgresql railway setup"
- "railway database backup"
- "prisma railway"
## Purpose
Manages PostgreSQL databases on Railway, including database creation, migrations, backups, connection management, and integration with ORMs (Prisma, TypeORM, Sequelize). Provides comprehensive database operations for applications deployed on Railway.
## Context
Railway provides managed PostgreSQL databases with automatic SSL encryption, private networking, and seamless integration with Railway services. The database automatically configures DATABASE_URL and provides both public and private connection options.
## Core Capabilities
### 1. Database Provisioning
- Add Postgres to Railway project
- Automatic DATABASE_URL configuration
- Private and public connection strings
- SSL-enabled connections
### 2. Connection Management
- Connect to database via Railway CLI
- Configure connection pooling
- Private network connections
- Public connections for external tools
### 3. Schema Management
- Run database migrations
- ORM integration (Prisma, TypeORM, Drizzle)
- Schema synchronization
- Database seeding
### 4. Backup & Recovery
- Manual backups via pg_dump
- Railway's native backup feature
- Point-in-time recovery
- Backup automation
### 5. Performance Optimization
- Connection pooling
- Query optimization
- Index management
- Performance monitoring
## Implementation Patterns
### 1. Add Postgres to Railway Project
```bash
# Add PostgreSQL database to your project
railway add --database postgres
# Railway automatically creates these variables:
# DATABASE_URL=postgresql://user:password@host:port/dbname
# DATABASE_PUBLIC_URL=postgresql://user:password@public-host:port/dbname
# DATABASE_PRIVATE_URL=postgresql://user:password@private-host:port/dbname
# PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE
```
### 2. Environment Variable Configuration
```env
# Railway provides these automatically
DATABASE_URL=${{Postgres.DATABASE_URL}}
# For private network (recommended for Railway services)
DATABASE_PRIVATE_URL=${{Postgres.DATABASE_PRIVATE_URL}}
# For external connections (pgAdmin, Postico, etc.)
DATABASE_PUBLIC_URL=${{Postgres.DATABASE_PUBLIC_URL}}
# Individual connection components
PGHOST=${{Postgres.PGHOST}}
PGPORT=${{Postgres.PGPORT}}
PGUSER=${{Postgres.PGUSER}}
PGPASSWORD=${{Postgres.PGPASSWORD}}
PGDATABASE=${{Postgres.PGDATABASE}}
# Connection pool settings
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10
DATABASE_SSL_REQUIRED=true
```
### 3. Prisma Integration
#### Schema Configuration
```prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
@@index([email])
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
authorId String
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
@@index([published])
}
```
#### Migration Workflow
```bash
# Generate migration locally
npx prisma migrate dev --name init
# Deploy migration on Railway
railway run npx prisma migrate deploy
# Generate Prisma Client
railway run npx prisma generate
# Seed database
railway run npx prisma db seed
```
#### Prisma Client Configuration
```typescript
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
declare global {
var prisma: PrismaClient | undefined;
}
export const prisma = global.prisma || new PrismaClient({
log: process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error'],
});
if (process.env.NODE_ENV !== 'production') {
global.prisma = prisma;
}
// Graceful shutdown
process.on('beforeExit', async () => {
await prisma.$disconnect();
});
```
#### Connection Pooling with Prisma
```typescript
// For serverless environments (high connection count)
// Use Prisma Data Proxy or connection pooling
import { PrismaClient } from '@prisma/client';
import { Pool } from 'pg';
// Option 1: Use external connection pooler (PgBouncer)
const connectionString = `${process.env.DATABASE_URL}?pgbouncer=true`;
export const prisma = new PrismaClient({
datasources: {
db: {
url: connectionString,
},
},
});
// Option 2: Use pg Pool with Prisma
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
min: 5,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
ssl: {
rejectUnauthorized: false,
},
});
```
### 4. TypeORM Integration
```typescript
// src/data-source.ts
import { DataSource } from 'typeorm';
import { User } from './entities/User';
import { Post } from './entities/Post';
export const AppDataSource = new DataSource({
type: 'postgres',
url: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
},
synchronize: process.env.NODE_ENV === 'development',
logging: process.env.NODE_ENV === 'development',
entities: [User, Post],
migrations: ['src/migrations/**/*.ts'],
subscribers: [],
extra: {
max: 10, // Connection pool size
min: 2,
},
});
// Initialize connection
export async function initializeDatabase(): Promise<void> {
try {
await AppDataSource.initialize();
console.log('Database connected successfully');
} catch (error) {
console.error('Database connection failed:', error);
throw error;
}
}
```
#### TypeORM Migrations
```bash
# Generate migration
railway run npm run typeorm migration:generate -- -n CreateUsers
# Run migrations
railway run npm run typeorm migration:run
# Revert migration
railway run npm run typeorm migration:revert
```
### 5. Drizzle ORM Integration
```typescript
// src/db/schema.ts
import { pgTable, serial, text, timestamp, boolean, index } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').notNull().unique(),
name: text('name'),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
}, (table) => ({
emailIdx: index('email_idx').on(table.email),
}));
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false),
authorId: serial('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
}, (table) => ({
authorIdx: index('author_idx').on(table.authorId),
publishedIdx: index('published_idx').on(table.published),
}));
```
```typescript
// src/db/client.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const connectionString = process.env.DATABASE_URL!;
// For query execution
const queryClient = postgres(connectionString, {
ssl: 'require',
max: 10,
idle_timeout: 20,
connect_timeout: 10,
});
export const db = drizzle(queryClient, { schema });
// For migrations
const migrationClient = postgres(connectionString, {
ssl: 'require',
max: 1,
});
export const migrationDb = drizzle(migrationClient);
```
#### Drizzle Migrations
```bash
# Generate migration
railway run npx drizzle-kit generate:pg
# Run migrations
railway run npx drizzle-kit push:pg
```
### 6. Raw PostgreSQL Connections
```typescript
// Using node-postgres (pg)
import { Pool, Client } from 'pg';
// Connection pool (recommended)
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
},
max: 20,
min: 5,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Query with pool
async function queryWithPool<T>(
text: string,
params?: any[]
): Promise<T[]> {
const client = await pool.connect();
try {
const result = await client.query(text, params);
return result.rows;
} finally {
client.release();
}
}
// Single client (for one-off queries)
async function queryWithClient<T>(
text: string,
params?: any[]
): Promise<T[]> {
const client = new Client({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
},
});
await client.connect();
try {
const result = await client.query(text, params);
return result.rows;
} finally {
await client.end();
}
}
// Transaction helper
async function transaction<T>(
callback: (client: Client) => Promise<T>
): Promise<T> {
const client = await pool.connect();
try {
await client.query('BEGIN');
const result = await callback(client);
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
```
### 7. Database Backup & Recovery
#### Manual Backup
```bash
# Connect to Railway Postgres
railway connect Postgres
# Backup database to file
pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql
# Backup with compression
pg_dump $DATABASE_URL | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
# Backup specific tables
pg_dump $DATABASE_URL --table=users --table=posts > backup_tables.sql
# Schema-only backup
pg_dump $DATABASE_URL --schema-only > schema_backup.sql
# Data-only backup
pg_dump $DATABASE_URL --data-only > data_backup.sql
```
#### Restore from Backup
```bash
# Restore from SQL file
psql $DATABASE_URL < backup.sql
# Restore from compressed file
gunzip -c backup.sql.gz | psql $DATABASE_URL
# Restore with Railway CLI
railway run psql $DATABASE_URL < backup.sql
```
#### Automated Backup Script
```typescript
// scripts/backup.ts
import { exec } from 'child_process';
import { promisify } from 'util';
import * as Minio from 'minio';
const execAsync = promisify(exec);
const minioClient = new Minio.Client({
endPoint: process.env.MINIO_ENDPOINT!,
port: parseInt(process.env.MINIO_PORT!),
useSSL: false,
accessKey: process.env.MINIO_ROOT_USER!,
secretKey: process.env.MINIO_ROOT_PASSWORD!,
});
async function backupDatabase(): Promise<void> {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `backup_${timestamp}.sql.gz`;
const localPath = `/tmp/${filename}`;
try {
// Create backup
console.log('Creating database backup...');
await execAsync(
`pg_dump ${process.env.DATABASE_URL} | gzip > ${localPath}`
);
// Upload to MinIO
console.log('Uploading backup to MinIO...');
await minioClient.fPutObject('backups', filename, localPath);
// Clean up local file
await execAsync(`rm ${localPath}`);
console.log(`Backup completed: ${filename}`);
} catch (error) {
console.error('Backup failed:', error);
throw error;
}
}
// Run backup
backupDatabase()
.then(() => process.exit(0))
.catch(() => process.exit(1));
```
### 8. Connection via Railway CLI
```bash
# Connect to Postgres shell
railway connect Postgres
# Once connected, you can run SQL:
\dt # List tables
\d users # Describe users table
\l # List databases
\du # List users
\q # Quit
# Run SQL queries
SELECT * FROM users LIMIT 10;
# Create index
CREATE INDEX idx_users_email ON users(email);
# Analyze query performance
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';
```
### 9. Performance Optimization
#### Connection Pooling Configuration
```typescript
// lib/db.ts
import { Pool } from 'pg';
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
// Pool configuration
max: 20, // Maximum pool size
min: 5, // Minimum pool size
idleTimeoutMillis: 30000, // Close idle connections after 30s
connectionTimeoutMillis: 2000, // Timeout connection attempts after 2s
// Statement timeout
statement_timeout: 10000, // 10 seconds
// Query timeout
query_timeout: 10000, // 10 seconds
});
// Handle pool errors
pool.on('error', (err) => {
console.error('Unexpected database pool error:', err);
});
// Graceful shutdown
process.on('SIGTERM', async () => {
await pool.end();
});
```
#### Query Optimization
```sql
-- Create indexes for common queries
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_author_id ON posts(author_id);
CREATE INDEX idx_posts_published ON posts(published) WHERE published = true;
-- Composite indexes
CREATE INDEX idx_posts_author_published ON posts(author_id, published);
-- Full-text search index
CREATE INDEX idx_posts_search ON posts USING gin(to_tsvector('english', title || ' ' || content));
-- Analyze query performance
EXPLAIN ANALYZE
SELECT p.*, u.name as author_name
FROM posts p
JOIN users u ON p.author_id = u.id
WHERE p.published = true
ORDER BY p.created_at DESC
LIMIT 10;
```
## Best Practices
### 1. Connection Management
- **Use connection pooling** for all production applications
- **Set appropriate pool sizes** (max 20 for most applications)
- **Configure timeouts** to prevent hanging connections
- **Use private networking** for Railway service connections
- **Close connections gracefully** on application shutdown
### 2. Migration Safety
- **Always backup** before running migrations
- **Test migrations** in staging environment first
- **Use transactions** in migrations when possible
- **Make migrations reversible** with down migrations
- **Run migrations** during low-traffic periods
### 3. Security
- **Never expose DATABASE_PUBLIC_URL** in client-side code
- **Use environment variables** for credentials
- **Enable SSL** for all connections
- **Rotate credentials** periodically
- **Use least privilege** for database users
- **Sanitize inputs** to prevent SQL injection
### 4. Performance
- **Create indexes** for frequently queried columns
- **Use prepared statements** to prevent SQL injection and improve performance
- **Implement query timeouts** to prevent long-running queries
- **Monitor slow queries** and optimize them
- **Use connection pooling** to reduce connection overhead
- **Consider read replicas** for read-heavy workloads (future Railway feature)
### 5. Backup Strategy
- **Automate backups** with scheduled tasks
- **Store backups** in multiple locations (MinIO + external)
- **Test restore procedures** regularly
- **Keep multiple backup versions** (7 daily, 4 weekly, 12 monthly)
- **Use Railway's native backup feature** for point-in-time recovery
### 6. Monitoring
- **Log slow queries** for optimization
- **Monitor connection pool usage**
- **Track database size growth**
- **Set up alerts** for connection pool exhaustion
- **Monitor query performance** with EXPLAIN ANALYZE
## Common Workflows
### 1. Initial Database Setup
```bash
# Add Postgres to project
railway add --database postgres
# Initialize Prisma (or your ORM)
npx prisma init
# Create migration
npx prisma migrate dev --name init
# Deploy to Railway
railway run npx prisma migrate deploy
railway run npx prisma generate
railway run npx prisma db seed
```
### 2. Running Migrations
```bash
# Test migration locally with Railway variables
railway run npx prisma migrate dev
# Deploy migration to production
railway environment production
railway run npx prisma migrate deploy
```
### 3. Database Seeding
```typescript
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// Create users
const users = await Promise.all([
prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
},
}),
prisma.user.create({
data: {
email: 'bob@example.com',
name: 'Bob',
},
}),
]);
// Create posts
await Promise.all([
prisma.post.create({
data: {
title: 'First Post',
content: 'Hello World!',
published: true,
authorId: users[0].id,
},
}),
prisma.post.create({
data: {
title: 'Second Post',
content: 'Railway is awesome!',
published: true,
authorId: users[1].id,
},
}),
]);
console.log('Database seeded successfully');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
```
```bash
# Run seed script
railway run npm run seed
```
### 4. Database Reset (Development Only)
```bash
# WARNING: This deletes all data!
railway environment development
railway run npx prisma migrate reset
```
### 5. Query Database from CLI
```bash
# Connect to database
railway connect Postgres
# Run queries
SELECT COUNT(*) FROM users;
SELECT * FROM posts WHERE published = true;
# Check database size
SELECT pg_size_pretty(pg_database_size(current_database()));
# List all tables
\dt
```
## Troubleshooting
### Connection Errors
- Verify DATABASE_URL is set correctly
- Check if using SSL (required by Railway)
- Ensure service is in same project for private networking
- Verify Railway Postgres service is running
- Check connection pool configuration
### Migration Failures
- Check migration syntax for errors
- Verify database schema state
- Ensure migrations are run in correct order
- Check for conflicting migrations
- Review migration logs for specific errors
### Performance Issues
- Review slow query logs
- Check for missing indexes
- Verify connection pool isn't exhausted
- Monitor Railway database metrics
- Use EXPLAIN ANALYZE to understand query plans
### SSL/TLS Errors
- Always set `ssl: { rejectUnauthorized: false }` for Railway
- Verify SSL is enabled in connection string
- Check if SSL certificates are up to date
## Resources
- Railway Postgres Docs: https://docs.railway.com/guides/postgresql
- Prisma Documentation: https://www.prisma.io/docs
- TypeORM Documentation: https://typeorm.io/
- Drizzle Documentation: https://orm.drizzle.team/
- PostgreSQL Documentation: https://www.postgresql.org/docs/
## Notes
- Railway Postgres includes automatic SSL encryption
- DATABASE_URL uses private networking by default
- Use DATABASE_PUBLIC_URL for external connections (pgAdmin, etc.)
- Railway provides automatic backups in Pro plans
- Connection pooling is essential for serverless deployments
- Always test migrations in staging before production