architecture
8 min read

Building Self-Sufficient Development Platforms: A Self-Hosting Journey

Learn how to build and maintain a self-hosted development platform using Docker, Coolify, and open-source alternatives to expensive SaaS tools. Infrastructure as craft, not chore.

Most developers depend on SaaS vendors for error tracking, analytics, and deployment. We pay monthly subscriptions for Sentry, Google Analytics, and deployment platforms, accepting vendor lock-in and limited customization as the cost of convenience. But what if you could build a self-sufficient platform that you control and understand?

This article explores the journey from SaaS dependency to infrastructure independence. You’ll learn how to set up a self-hosted development platform with Docker and Coolify, including error tracking (GlitchTip), analytics (Matomo), and deployment automation. More importantly, you’ll understand the infrastructure-as-craft mindset and learn when self-hosting makes sense versus when SaaS is the right choice.

Why Self-Hosting? The Infrastructure Independence Mindset

You’re building a personal platform empire—not dependent on SaaS vendors, but on systems you control and understand. This isn’t about rejecting SaaS entirely; it’s about understanding your stack and having the option to own your infrastructure when it matters.

The Cost Reality

Let’s start with the numbers. A typical developer might pay:

  • Sentry: $26/month for error tracking
  • Google Analytics: Free, but with privacy concerns
  • Deployment platform: $20-50/month
  • Total: $46-76/month, or $552-912/year

With self-hosting, you’re paying for:

  • A VPS: $5-10/month
  • Domain: $12/year
  • Total: $60-132/year

The cost savings are real, but they’re not the only benefit. More importantly, you’re not locked into vendor pricing changes, feature limitations, or service outages.

Control and Customization

When you self-host, you control:

  • Data ownership: Your error logs, analytics data, and user data stays with you
  • Feature customization: Need a specific feature? You can add it or modify the open-source tools
  • Privacy compliance: GDPR, CCPA, and other regulations are easier when you control the data
  • Uptime: You’re not dependent on a third-party service being available

Learning Opportunity

Self-hosting forces you to understand your stack. You’ll learn:

  • Docker containerization patterns
  • Database management and backups
  • SSL certificate automation
  • Monitoring and alerting
  • System administration basics

This knowledge makes you a better developer, even if you eventually move back to managed services.

When Self-Hosting Makes Sense

Self-hosting is ideal when:

  • You need data privacy and control
  • You want to learn infrastructure skills
  • You have predictable, manageable traffic
  • You want to avoid vendor lock-in
  • You need custom features not available in SaaS

Self-hosting doesn’t make sense when:

  • You need enterprise-grade reliability and support
  • You don’t have time to maintain infrastructure
  • Your traffic is unpredictable or high-volume
  • You need compliance certifications (SOC 2, etc.)
  • You’re building an MVP and need to move fast

The key is understanding the trade-offs and choosing based on your specific needs, not ideology.

The Core Stack: Docker + Coolify

Docker is the foundation of modern self-hosting. It provides containerization that makes deployment consistent and reproducible. Coolify is a self-hosted deployment platform that simplifies Docker orchestration without the complexity of Kubernetes.

Docker as the Foundation

Docker containers package your application with all its dependencies, ensuring it runs the same way in development, staging, and production. This consistency eliminates “works on my machine” problems and makes deployment predictable.

Here’s a typical Dockerfile pattern from a production project:

FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM node:20-alpine

WORKDIR /app

# Copy only production dependencies
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./

# Expose port
EXPOSE 3000

# Start the application
CMD ["node", "dist/index.js"]

This multi-stage build keeps the final image small by excluding development dependencies and build tools.

docker-compose for Local Development

For local development, docker-compose simplifies running multiple services together:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
    depends_on:
      - db
    volumes:
      - ./src:/app/src

  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

This setup lets you run your entire stack locally with a single command: docker-compose up.

Coolify: Self-Hosted Deployment

Coolify is a self-hosted alternative to platforms like Vercel or Railway. It provides:

  • One-click deployments from Git repositories
  • Automatic SSL certificate management (Let’s Encrypt)
  • Environment variable management
  • Database provisioning
  • Health monitoring

The beauty of Coolify is that it runs on your own server, giving you the convenience of a managed platform with the control of self-hosting.

CoolerCoolify: Enhancing the Tools You Use

One project that demonstrates the infrastructure-as-craft mindset is CoolerCoolify—a Laravel package that enhances Coolify’s UI with search, sorting, drag-and-drop, and icon support. This project shows that you’re not just using tools; you’re improving them, wrapping them, making them yours.

When you self-host, you can customize and enhance the tools you use. You’re not limited by what a SaaS vendor provides.

Building the Monitoring Stack

A self-hosted platform needs monitoring. You need to know when things break, how your applications perform, and what your users are doing.

GlitchTip: Sentry-Compatible Error Tracking

GlitchTip is a self-hosted, Sentry-compatible error tracking service. It provides the same API and features as Sentry, so you can use the same SDKs in your applications.

Setting up GlitchTip with Docker is straightforward:

version: '3.8'

services:
  glitchtip:
    image: glitchtip/glitchtip:latest
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/glitchtip
      - SECRET_KEY=your-secret-key
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=glitchtip

Once deployed, you can use GlitchTip exactly like Sentry in your applications:

import * as Sentry from "@sentry/node";

Sentry.init({
  dsn: "https://your-key@glitchtip.example.com/1",
  environment: process.env.NODE_ENV,
});

Matomo: Privacy-Focused Web Analytics

Matomo (formerly Piwik) is a privacy-focused alternative to Google Analytics. It’s GDPR-compliant, doesn’t use cookies by default, and gives you complete control over your analytics data.

Matomo provides:

  • Real-time visitor tracking
  • Custom dashboards and reports
  • E-commerce tracking
  • Heatmaps and session recordings (with plugins)
  • A/B testing capabilities

Deploying Matomo with Docker:

version: '3.8'

services:
  matomo:
    image: matomo:latest
    environment:
      - MATOMO_DATABASE_HOST=db
      - MATOMO_DATABASE_ADAPTER=mysql
      - MATOMO_DATABASE_TABLES_PREFIX=matomo_
    ports:
      - "8080:80"
    depends_on:
      - db

  db:
    image: mariadb:10.11
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
      - MYSQL_DATABASE=matomo
      - MYSQL_USER=matomo
      - MYSQL_PASSWORD=matomopassword

Health Monitoring and Alerting

Beyond error tracking and analytics, you need health monitoring. Coolify provides basic health checks, but you can enhance this with:

  • Uptime monitoring: Services like UptimeRobot (free tier) or self-hosted Uptime Kuma
  • Log aggregation: Centralized logging with Loki or ELK stack
  • Metrics collection: Prometheus for metrics, Grafana for visualization

The key is starting simple and adding complexity only when needed. A basic health check endpoint in your application is often enough:

app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
  });
});

SSL Automation with Coolify

One of Coolify’s best features is automatic SSL certificate management. When you deploy a new service, Coolify automatically:

  1. Detects your domain
  2. Requests a Let’s Encrypt certificate
  3. Configures SSL/TLS
  4. Renews certificates automatically

This eliminates one of the biggest pain points of self-hosting: SSL certificate management.

Database and Data Layer

Your self-hosted platform needs reliable data storage. The choice of database depends on your use case, but PostgreSQL is an excellent default choice.

PostgreSQL for Primary Data

PostgreSQL is the database of choice for most modern applications. It’s:

  • Open-source and battle-tested
  • Feature-rich (JSON support, full-text search, extensions)
  • Performant and scalable
  • Well-supported by ORMs like Prisma

In the OpsWerk project, PostgreSQL serves as the primary database for GlitchTip and other services. It’s reliable, performant, and has excellent tooling.

MariaDB for Specific Use Cases

MariaDB (a MySQL fork) is useful when:

  • You’re migrating from MySQL
  • You need MySQL compatibility
  • You’re using tools that require MySQL (like some CMS platforms)

In OpsWerk, MariaDB is used alongside PostgreSQL for services that specifically require MySQL compatibility.

Redis/Valkey for Caching

Redis (or its fork Valkey) is essential for:

  • Session storage
  • Caching frequently accessed data
  • Rate limiting
  • Real-time features (pub/sub)

A typical Redis setup in docker-compose:

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  redis_data:

Backup Strategies

Backups are critical for self-hosted infrastructure. Your backup strategy should include:

  1. Database backups: Automated daily backups with retention policies
  2. Volume backups: Backup Docker volumes regularly
  3. Configuration backups: Version control for docker-compose files and configurations
  4. Off-site storage: Store backups in a different location (S3, Backblaze, etc.)

Here’s a simple backup script for PostgreSQL:

#!/bin/bash
BACKUP_DIR="/backups/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
docker exec postgres_container pg_dump -U user database_name > "$BACKUP_DIR/backup_$DATE.sql"
# Keep only last 7 days
find "$BACKUP_DIR" -name "backup_*.sql" -mtime +7 -delete

Database Migration Patterns

When you self-host, you’re responsible for database migrations. Use migration tools like:

  • Prisma Migrate: For Prisma-based applications
  • Laravel Migrations: For Laravel applications
  • Flyway or Liquibase: For database-first approaches

Always test migrations in a staging environment before production.

Real-World Application: Revolver API Case Study

Revolver API demonstrates a complete self-hosted API stack. It uses PostgREST to auto-generate a REST API from a PostgreSQL database schema, combined with an admin UI and webhook bridge service.

PostgREST: Database-as-API

PostgREST is a web server that automatically generates a REST API from your PostgreSQL database schema. Instead of writing API endpoints manually, you define your database schema, and PostgREST exposes it as a REST API.

Here’s how it works:

  1. Define your schema: Create tables, views, and functions in PostgreSQL
  2. Configure PostgREST: Point it to your database
  3. Get instant API: REST endpoints are automatically available

Example schema:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  title TEXT NOT NULL,
  content TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

PostgREST automatically exposes:

  • GET /users - List all users
  • GET /users/:id - Get a specific user
  • POST /users - Create a user
  • PATCH /users/:id - Update a user
  • DELETE /users/:id - Delete a user
  • GET /posts?user_id=eq.1 - Get posts for user 1

Self-Hosted API Stack Architecture

Revolver API’s architecture includes:

  1. PostgreSQL 16: The database layer
  2. PostgREST 12: Auto-generated REST API
  3. Node.js/Express Bridge: Custom endpoints and webhook handling
  4. React Admin UI: Data management interface
  5. Docker Containers: All services containerized

This stack provides:

  • Auto-generated REST API (no manual endpoint coding)
  • Type-safe database access
  • Admin interface for data management
  • Webhook support for integrations
  • JWT authentication

Admin UI Integration

The admin UI in Revolver API provides:

  • Data browser for viewing and editing records
  • Query builder for complex queries
  • User management
  • API key management

This demonstrates that self-hosted doesn’t mean basic. You can build sophisticated admin interfaces on top of your self-hosted infrastructure.

Webhook Bridge Service

The webhook bridge service handles:

  • Receiving webhooks from external services
  • Transforming webhook payloads
  • Forwarding to PostgREST or custom endpoints
  • Retry logic and error handling

This pattern is common in self-hosted setups where you need to integrate with external services while maintaining control over your data flow.

JWT Authentication Patterns

PostgREST supports JWT authentication. You configure it to validate JWTs and use claims for row-level security:

-- Enable row-level security
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see their own posts
CREATE POLICY user_posts ON posts
  FOR SELECT
  USING (user_id = current_setting('request.jwt.claim.user_id')::integer);

This provides secure, fine-grained access control without writing custom authentication logic.

Common Pitfalls

Self-hosting comes with challenges. Here are the most common pitfalls and how to avoid them:

Over-Engineering the Initial Setup

It’s tempting to set up everything at once: monitoring, logging, metrics, alerting, backups, and more. Start simple:

  1. Deploy your application
  2. Add error tracking
  3. Add basic monitoring
  4. Add backups
  5. Expand from there

Don’t build a complex infrastructure before you need it.

Neglecting Backups and Monitoring

Backups and monitoring are non-negotiable. Set them up from day one:

  • Automated database backups
  • Volume backups for persistent data
  • Health checks and uptime monitoring
  • Error tracking

Without these, you’re one failure away from data loss or extended downtime.

Security Misconfigurations

Common security mistakes:

  • Exposing services without authentication
  • Using default passwords
  • Not keeping containers updated
  • Exposing database ports publicly
  • Weak SSL/TLS configurations

Always:

  • Use strong, unique passwords
  • Enable authentication on all services
  • Keep containers and base images updated
  • Use firewall rules to restrict access
  • Enable SSL/TLS everywhere

Resource Allocation Mistakes

Self-hosting on a VPS means limited resources. Common mistakes:

  • Running too many services on one server
  • Not monitoring resource usage
  • Not planning for traffic spikes
  • Ignoring disk space

Monitor your resource usage and scale appropriately. Sometimes, splitting services across multiple servers is necessary.

When to Use Managed Services Instead

Self-hosting isn’t always the answer. Use managed services when:

  • You need enterprise-grade reliability
  • You don’t have time for maintenance
  • You need compliance certifications
  • Your traffic is unpredictable
  • You’re building an MVP

The goal isn’t to self-host everything; it’s to understand your infrastructure and make informed choices.

Conclusion

Self-hosting isn’t about rejecting SaaS—it’s about building infrastructure you understand and control. With Docker and Coolify, you can create a self-sufficient development platform that grows with your needs.

The key takeaways:

  • Infrastructure as craft mindset: Infrastructure isn’t a chore; it’s part of the product
  • Practical stack: Docker + Coolify + monitoring provides a solid foundation
  • Real production examples: OpsWerk and Revolver API show what’s possible

Start with one service—error tracking or analytics—containerize it, and expand from there. The infrastructure independence journey begins with a single container. You’re building a personal platform empire, one service at a time.

#Docker #DevOps #Self-Hosting #Coolify #Infrastructure #PostgreSQL
Share: