DEV Community

Cover image for Staxless: Your Scalable SaaS Starter Kit for Rapid Development and Deployment
Zach Benson
Zach Benson

Posted on

Staxless: Your Scalable SaaS Starter Kit for Rapid Development and Deployment

How Staxless Was Built

Staxless was born from my journey as a SaaS founder (@zach_benson817). After a year of battling complex microservice setups, I wanted freedom, flexibility, and ownership—a codebase that lets solo founders build fast without drowning in infrastructure. Think of Staxless as a LEGO set for SaaS scaling: each piece (microservice) fits perfectly, but you can swap or customize them as your app grows. Here’s how we built it:

Architecture Diagram

  • Frontend with Next.js: Micro frontend components fetch products and auth providers, styled rapidly with Tailwind CSS for consistent branding.
  • Authentication: Passwordless OAuth 2.0 and magic links, configured quickly and securely.
  • Event-Driven Architecture with Kafka: Kafka enables asynchronous communication between services, ensuring loose coupling and scalability.
  • Payments with Stripe: A Stripe microservice manages checkout and webhooks, syncing subscriptions to MongoDB via Kafka events.
  • Content with Payload CMS: Payload CMS delivers docs, legal pages, and blogs through a user-friendly admin UI and GraphQL API.
  • Emails with Mailgun: Pre-configured templates simplify sending magic links and purchase confirmations.
  • Dockerized Deployment: Docker Compose and Docker Swarm streamline local and production setups for seamless deployment.

Staxless focused on simplicity—no over-engineered solutions, just a practical template for SaaS founders. Every component is swappable, so you’re never locked in. Want to replace MongoDB with PostgreSQL? Go for it. Prefer SendGrid over Mailgun? Easy swap. Staxless is your blank canvas, not a rigid framework.


Getting Started with Development

Ready to kick the tires on Staxless? Setting up your local development environment is straightforward, thanks to the pre-configured codebase and Docker Compose. Here’s how to get rolling in five simple steps, so you can start building your SaaS faster than you can say “monolith meltdown.”

getting started

  1. Clone the Staxless Repo: Grab the codebase from the Staxless GitHub repo (included in your Basic or Premium plan).
   git clone https://github.com/staxless/staxless.git
   cd staxless
Enter fullscreen mode Exit fullscreen mode
  1. Install Dependencies: Ensure you have Docker, Docker Compose, and Node.js (LTS version) installed. Then, install frontend dependencies:
   cd frontend
   npm install
   cd ..
Enter fullscreen mode Exit fullscreen mode
  1. Set Up Environment Variables: Copy the template env file and add your credentials (e.g., Mailgun, Stripe, OAuth keys) from /config/secrets/dev/staxless.dev.env.template to /config/secrets/dev/staxless.dev.env. Example:
   # /config/secrets/dev/staxless.dev.env
   NODE_ENV=development
   DATABASE_URI=mongodb://mongodb-service/database
   MAILGUN_API_KEY=your-mailgun-api-key
   STRIPE_PRIVATE_KEY=your-stripe-private-key
   GOOGLE_CLIENT_ID=your-google-client-id
   GOOGLE_CLIENT_SECRET=your-google-client-secret
   GITHUB_CLIENT_ID=your-github-client-id
   GITHUB_CLIENT_SECRET=your-github-client-secret
Enter fullscreen mode Exit fullscreen mode
  1. Launch the Stack with Docker Compose: Start all services (frontend, auth, user, stripe, email, cms, Kafka, MongoDB) in development mode:
   npm run staxless-dev-build
Enter fullscreen mode Exit fullscreen mode

This spins up the services, exposes ports (e.g., 3000 for frontend, 8000 for gateway, 8080 for auth), and creates Kafka topics like project-events (see /config/docker-compose.dev.yml).

  1. Test the Setup: Verify the services are running by checking health endpoints via Kong:
   curl http://localhost:8000/auth/health
   curl http://localhost:8000/users/health
   curl http://localhost:8000/email/health
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000 in your browser to see the Next.js frontend in action.

Now you’re ready to customize Staxless for your SaaS! Edit /frontend/src/lib/brand-config.ts for branding or add new microservices (like the Project Sharing feature below). Check /config/test.sh for end-to-end tests to ensure everything’s humming. Got stuck? The Premium plan’s community forum has your back.

Console


ConnectSphere: A Hypothetical SaaS Demo

Let’s bring Staxless to life with ConnectSphere, a community platform for indie makers to collaborate and share projects. ConnectSphere’s tagline—“Build Together, Thrive Together”—captures its mission to foster creative connections. With Staxless, we’ll set up ConnectSphere’s branding, authentication, Stripe, Mailgun, and a custom Project Sharing feature, leveraging Kafka for seamless backend communication. Here’s how:

Branding ConnectSphere

Customizing ConnectSphere’s look and feel is a snap with Staxless’s centralized branding config in /frontend/src/lib/brand-config.ts. We define the company details, colors, and SEO settings, which cascade across the Next.js frontend (e.g., header.tsx, footer.tsx). Here’s the simplified configuration:

// /frontend/src/lib/brand-config.ts
export const BRAND_CONFIG = {
  company: {
    name: 'ConnectSphere',
    tagline: 'Build Together, Thrive Together',
    description: 'A community platform for indie makers to collaborate and share projects.',
    website: 'https://connectsphere.app',
    email: 'hello@connectsphere.app',
    phone: '+1 (555) 987-6543',
    address: '456 Stellar Ave, Galaxy City, State 67890',
  },
  style: {
    colors: {
      primary: '#4A90E2', // Vibrant blue for CTAs
      primaryHover: '#357ABD', // Darker blue for hover
      background: '#F7FAFC', // Light blue-gray background
      textPrimary: '#1A202C', // Near-black for main text
      accent: '#A855F7', // Warm purple for highlights
    },
  },
  seo: {
    title: 'ConnectSphere - Collaborate & Share Projects',
    description: 'Join indie makers to build, share, and thrive together.',
    keywords: ['indie makers', 'community', 'collaboration'],
    ogImage: '/connectsphere-og.png',
  },
};
Enter fullscreen mode Exit fullscreen mode

We drop connectsphere-logo.png into /frontend/public/ and apply styles in /frontend/src/app/globals.css using Tailwind classes (e.g., bg-[#F7FAFC] for the background). The result? A branded dashboard with vibrant blue CTAs, a sleek logo, and consistent styling across pages like the homepage, pricing, and features.

Example Home

Authentication Setup

ConnectSphere needs secure, scalable auth. Staxless’s LUCIA auth module delivers passwordless OAuth2.0 Magic Links with Google and GitHub providers. We deploy the auth microservice using Docker Compose and configure OAuth in /microservices/auth-service/src/config/oauth-config.ts. Here’s the setup:

  1. Spin Up the Auth Service:
   docker-compose -f config/docker-compose.dev.yml up -d auth-service
Enter fullscreen mode Exit fullscreen mode

This starts the auth service on port 8080, connected to Kafka and MongoDB.

  1. Configure OAuth Providers:
   // /microservices/auth-service/src/config/oauth-config.ts
   import { google, github } from 'lucia-auth/oauth';
   export const googleAuth = google({
     clientId: process.env.GOOGLE_CLIENT_ID,
     clientSecret: process.env.GOOGLE_CLIENT_SECRET,
     redirectUri: "http://localhost:8000/auth/google/callback",
   });
   export const githubAuth = github({
     clientId: process.env.GITHUB_CLIENT_ID,
     clientSecret: process.env.GITHUB_CLIENT_SECRET,
     redirectUri: "http://localhost:8000/auth/github/callback",
   });
Enter fullscreen mode Exit fullscreen mode
  1. Test Magic Link Flow: A user clicks “Sign In” on /frontend/src/app/(auth)/login/page.tsx, triggering a magic link email via the email service (/microservices/email-service/src/consumers/handlers/handle-send-magic-link.ts). The Kafka consumer processes the email-events topic, sending the email through Mailgun.

Auth

The login page (styled with Tailwind) looks clean and branded, thanks to the config in /frontend/src/lib/brand-config.ts. Scalability? Handled. LUCIA’s session management and Kafka’s event-driven flow ensure ConnectSphere’s micro frontend component, pulling available providers from the backend, can handle thousands of users without breaking a sweat.

Stripe Setup

To enable payments for ConnectSphere (e.g., premium memberships), we configure the Stripe microservice to handle products, subscriptions, and checkout. Here’s how to set it up:

  1. Get a Testing API Key:

    • Sign up or log into your Stripe account at stripe.com.
    • Navigate to Developers > API Keys in the Stripe Dashboard.
    • Copy the Test Secret Key (starts with sk_test_) and add it to /config/secrets/dev/staxless.dev.env:
     STRIPE_PRIVATE_KEY=sk_test_your-test-secret-key
    
  2. Create a Product or Subscription:

    • In the Stripe Dashboard, go to Products and click Add Product.
    • Create a product (e.g., “ConnectSphere Premium”) with a recurring price (e.g., $10/month) or a one-time payment.
    • Copy the Price ID (starts with price_) for use in the frontend or backend configuration.
    • Update /microservices/stripe-service/src/config/stripe-config.ts with the Price ID if needed:
     export const STRIPE_CONFIG = {
       premiumPriceId: 'price_your-price-id',
     };
    
  3. Enable the Dynamic Pricing Table:

    • With the Stripe API key and product configured, the frontend (/frontend/src/app/(protected)/pricing/page.tsx) automatically fetches products from the Stripe microservice (/microservices/stripe-service/src/routes/stripe-routes.ts) and renders a dynamic pricing table styled with Tailwind CSS.
    • Test the pricing page at http://localhost:3000/pricing to see the table populated with your product (e.g., ConnectSphere Premium at $10/month).
    • When a user selects a plan, the Stripe microservice handles checkout via a POST /stripe/checkout endpoint, triggering a Kafka payment-events topic to sync subscription data to MongoDB.

This setup ensures ConnectSphere’s payment system is ready to handle subscriptions or one-time purchases, with webhooks (/microservices/stripe-service/src/routes/webhook-routes.ts) keeping user data in sync.

Pricing Table

Mailgun Setup

To send emails like magic links and purchase confirmations for ConnectSphere, we configure the Mailgun microservice with pre-built templates. Here’s how to set it up:

  1. Get Environment Variables:

    • Sign up for a Mailgun account at mailgun.com.
    • Navigate to Sending > Domains in the Mailgun Dashboard and add a domain (e.g., mg.connectsphere.app or use the sandbox domain for testing).
    • Copy the API Key and Domain Name from Sending > Domain Settings.
    • Add these to /config/secrets/dev/staxless.dev.env:
     MAILGUN_API_KEY=your-mailgun-api-key
     MAILGUN_DOMAIN=mg.connectsphere.app
    
  2. Create Email Templates:

    • In the Mailgun Dashboard, go to Sending > Templates and create two templates:
      • Magic Link Template: Named magic-link, with variables {{token}} and {{link}} (e.g., <a href="{{link}}">Sign In</a>).
      • Purchase Confirmation Template: Named purchase-confirmation-template, with variables {{orderId}} and {{items}} (e.g., “Thank you for your purchase! Order: {{orderId}}”).
    • These templates are pre-configured in /microservices/email-service/src/config/templateConfig.ts:
     export const templateConfig: TemplateConfigMap = {
       'magic-link': {
         templateName: 'magic-link',
         requiredVariables: ['token', 'link'],
         subject: 'Your Magic Link',
       },
       'purchase-confirmation': {
         templateName: 'purchase-confirmation-template',
         requiredVariables: ['orderId', 'items'],
         subject: 'Purchase Confirmation',
       },
       'welcome': {
         templateName: 'welcome-template',
         requiredVariables: ['name'],
         subject: 'Welcome to Our App!',
       },
     };
    
  3. Add Custom Templates (Optional):

    • To add more email templates (e.g., for project notifications), create a new template in Mailgun with required variables (e.g., {{projectTitle}}).
    • Update /microservices/email-service/src/config/templateConfig.ts with the new template:
     export const templateConfig: TemplateConfigMap = {
       ...templateConfig,
       'project-notification': {
         templateName: 'project-notification-template',
         requiredVariables: ['projectTitle', 'userName'],
         subject: 'New Project Shared!',
       },
     };
    
  • Register the template in the email service’s consumer (/microservices/email-service/src/consumers/handlers/).
  1. Test Email Sending:
    • Trigger a magic link email by attempting a login at http://localhost:3000/login.
    • The email service consumes the email-events topic via Kafka and sends the email using Mailgun.
    • Check your inbox (or Mailgun’s Sending > Logs) to verify delivery.

This setup ensures ConnectSphere can send branded, reliable emails for authentication and transactions, with the flexibility to add custom templates as needed.

Adding a Custom Feature: Project Sharing with Kafka Integration

ConnectSphere’s killer feature is Project Sharing, letting users post and upvote projects (think Product Hunt for indie makers). We create a new microservice using Staxless’s Premium tier code snippets, integrating it with the existing architecture and leveraging Kafka for asynchronous communication between the project service and user service (e.g., to track user activity or notify followers). Here’s how:

  1. Create the Project Service: We scaffold a new Node Express microservice in /microservices/project-service/, modeled after user-service. The Dockerfile (/config/dockerfiles/project-service.Dockerfile) mirrors user-service.Dockerfile:
   FROM node:lts AS builder
   USER node
   WORKDIR /home/node
   COPY --chown=node:node ["../package.json", "./"]
   RUN ["npm", "install"]
   COPY --chown=node:node ["../", "."]
   FROM builder AS dev
   RUN ["npm", "install", "nodemon", "--save-dev"]
   EXPOSE 8086
   CMD ["npm", "run", "dev"]
   FROM node:alpine AS prod
   USER node
   WORKDIR /home/node
   COPY --chown=node:node --from=builder /home/node/ ./
   CMD ["npm", "run", "prod"]
Enter fullscreen mode Exit fullscreen mode
  1. Define the Project Model: In /microservices/project-service/src/models/project-model.ts, we define a MongoDB schema:
   import { MongoClient, ObjectId } from 'mongodb';
   import { connectDB } from '../config/db-config';
   interface ProjectType {
     _id: ObjectId;
     title: string;
     description: string;
     userId: string;
     upvotes: number;
     created_at: Date;
   }
   const projectSchema = {
     bsonType: 'object',
     required: ['title', 'description', 'userId', 'upvotes', 'created_at'],
     properties: {
       _id: { bsonType: 'objectId' },
       title: { bsonType: 'string' },
       description: { bsonType: 'string' },
       userId: { bsonType: 'string' },
       upvotes: { bsonType: 'int' },
       created_at: { bsonType: 'date' },
     },
   };
   let db: MongoClient['db'];
   const initializeDatabase = async () => {
     const connection = await connectDB();
     db = connection.db(process.env.DATABASE_NAME);
     const collections = await db.listCollections({ name: 'projects' }).toArray();
     if (collections.length === 0) {
       await db.createCollection('projects', { validator: { $jsonSchema: projectSchema } });
     }
   };
   initializeDatabase().catch(() => {});
   export const Project = {
     async create(projectData: Omit<ProjectType, '_id'>) {
       projectData.created_at = new Date();
       projectData.upvotes = 0;
       const result = await db.collection('projects').insertOne(projectData);
       return result;
     },
     async upvote(projectId: string, userId: string) {
       const result = await db.collection('projects').updateOne(
         { _id: new ObjectId(projectId), userId: { $ne: userId } },
         { $inc: { upvotes: 1 } }
       );
       return result;
     },
   };
Enter fullscreen mode Exit fullscreen mode
  1. Set Up Kafka Producer for Project Events: In /microservices/project-service/src/config/kafka-client.ts, we configure a Kafka producer (similar to /microservices/stripe-service/src/config/kafka-client.ts):
   import { Kafka, Producer } from 'kafkajs';
   const kafka = new Kafka({
     clientId: 'staxless-app',
     brokers: ['kafka:29092'],
   });
   export const createProducer = (): Producer => {
     return kafka.producer();
   };
Enter fullscreen mode Exit fullscreen mode
  1. Add Project Routes with Kafka Publishing: In /microservices/project-service/src/routes/project-routes.ts, we define endpoints that publish events to Kafka’s project-events topic:
   import express from 'express';
   import { Project } from '../models/project-model';
   import { createProducer } from '../config/kafka-client';
   import logger from '../lib/logger';
   const router = express.Router();
   const producer = createProducer();
   router.post('/projects', async (req, res) => {
     try {
       const project = await Project.create(req.body);
       await producer.connect();
       await producer.send({
         topic: 'project-events',
         messages: [
           {
             key: 'project.created',
             value: JSON.stringify({
               projectId: project.insertedId,
               userId: req.body.userId,
               title: req.body.title,
               timestamp: new Date().toISOString(),
             }),
           },
         ],
       });
       await producer.disconnect();
       res.json(project);
     } catch (_error) {
       logger.error('Failed to create project or publish event', _error);
       res.status(500).json({ error: 'Failed to create project' });
     }
   });
   router.post('/projects/:id/upvote', async (req, res) => {
     try {
       const { id } = req.params;
       const userInfo = JSON.parse(req.headers['x-user-info'] as string);
       const result = await Project.upvote(id, userInfo.user_id);
       await producer.connect();
       await producer.send({
         topic: 'project-events',
         messages: [
           {
             key: 'project.upvoted',
             value: JSON.stringify({
               projectId: id,
               userId: userInfo.user_id,
               timestamp: new Date().toISOString(),
             }),
           },
         ],
       });
       await producer.disconnect();
       res.json(result);
     } catch (_error) {
       logger.error('Failed to upvote project or publish event', _error);
       res.status(500).json({ error: 'Failed to upvote project' });
     }
   });
   export default router;
Enter fullscreen mode Exit fullscreen mode
  1. Set Up Kafka Consumer in User Service: To track user activity (e.g., project posts or upvotes), we add a Kafka consumer to the user service in /microservices/user-service/src/consumers/handlers/handle-project-activity.ts:
   import type { KafkaMessage } from 'kafkajs';
   import { User } from '../../models/user-model';
   import logger from '../../lib/logger';
   export default async function handleProjectActivity(message: KafkaMessage) {
     try {
       const { projectId, userId, title, timestamp } = JSON.parse(message.value?.toString());
       const user = await User.findOne({ _id: userId });
       if (user) {
         await User.updateSubscription(userId, {
           ...user.subscription,
           lastActivity: timestamp,
         });
         logger.info(`Updated user ${userId} activity for project ${projectId}`);
       }
     } catch (_error) {
       logger.error('Error processing project activity event', _error);
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. Register the Consumer: In /microservices/user-service/src/consumers/handlers/index.ts, we update the event registry:
   import type { KafkaMessage } from 'kafkajs';
   import handleSubscriptionUpdate from './handle-subscription-update';
   import handleProjectActivity from './handle-project-activity';
   export const paymentEventRegistry = new Map<
     string,
     (message: KafkaMessage) => Promise<void>
   >([
     ['user.subscription.updated', handleSubscriptionUpdate],
     ['project.created', handleProjectActivity],
     ['project.upvoted', handleProjectActivity],
   ]);
Enter fullscreen mode Exit fullscreen mode
  1. Update Docker Compose: We update /config/docker-compose.dev.yml to include the project service and Kafka topic setup:
   services:
     project-service:
       image: app_project_service
       build:
         context: ../microservices/project-service/
         dockerfile: ../../config/dockerfiles/project-service.Dockerfile
         target: dev
       env_file:
         - ../config/secrets/dev/staxless.dev.env
       ports:
         - "8086:8086"
       expose:
         - "8086"
       volumes:
         - ../microservices/project-service/src:/home/node/src
       environment:
         - NODE_ENV=development
       networks:
         backend:
           aliases:
             - "project"
     kafka-setup:
       image: confluentinc/cp-kafka:7.5.0
       depends_on:
         - kafka-service
       networks:
         - backend
       command: >
         bash -c "
           echo 'Waiting for Kafka to be ready...' &&
           cub kafka-ready -b kafka:29092 1 20 &&
           echo 'Kafka is ready!' &&
           kafka-topics --create --topic auth-events --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           kafka-topics --create --topic email-events --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           kafka-topics --create --topic payment-events --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           kafka-topics --create --topic user-events --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           kafka-topics --create --topic project-events --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           kafka-topics --create --topic session-validation-replies --bootstrap-server kafka:29092 --partitions 1 --replication-factor 1 &&
           echo 'Topics created successfully.'
         "
Enter fullscreen mode Exit fullscreen mode
  1. Test the API via Kong: With Kong running, we test the project creation endpoint, which triggers a Kafka event:
   curl -X POST http://localhost:8000/projects -H "Content-Type: application/json" -H "x-user-info: {\"user_id\": \"60d5f1b4e6b3f1b3f8f1b3f8\"}" -d '{"title": "My Indie Project", "description": "A cool app for makers", "userId": "60d5f1b4e6b3f1b3f8f1b3f8"}'
Enter fullscreen mode Exit fullscreen mode
  1. Verify Kafka Event: We check the project-events topic to confirm the event was published:
   docker exec staxless-kafka-1 kafka-console-consumer --bootstrap-server localhost:29092 --topic project-events --from-beginning --timeout-ms 5000
Enter fullscreen mode Exit fullscreen mode

Output:

   {"projectId":"507f1f77bcf86cd799439011","userId":"60d5f1b4e6b3f1b3f8f1b3f8","title":"My Indie Project","timestamp":"2025-07-08T06:06:00.000Z"}
Enter fullscreen mode Exit fullscreen mode

The Project Sharing feature leverages Kafka to decouple the project service (handling posts and upvotes) from the user service (tracking activity). When a user posts a project, the project service publishes a project.created event to the project-events topic. The user service consumes this event, updating the user’s lastActivity field in MongoDB. This async communication ensures ConnectSphere scales effortlessly, even with thousands of projects and users. The frontend (/frontend/src/app/(protected)/dashboard/page.tsx) displays projects in a Tailwind-styled grid, branded with ConnectSphere’s blue palette.

Data Flow

[Frontend] --> [Kong Gateway] --> [Project Service]
                                      |
                                      v
                               [Kafka: project-events]
                                      |
                                      v
                               [User Service] --> [MongoDB]
Enter fullscreen mode Exit fullscreen mode

Outcome

With Staxless, ConnectSphere went from concept to a scalable, branded SaaS in days, not months. The Kafka-driven Project Sharing feature ensures loose coupling and scalability, while Stripe and Mailgun setups enable seamless payments and email communication. The modular architecture lets you swap components (e.g., add Firebase for real-time notifications) without refactoring. Founders can focus on community-building—engaging makers, iterating features, and sharing their #BuildInPublic journey—while Staxless handles the heavy lifting.


Deploying via Digital Ocean

Ready to take ConnectSphere live? Staxless’s Dockerized setup makes deployment on Digital Ocean a breeze, whether you use a Droplet (virtual machine) or App Platform (PaaS). Here’s how to deploy your Staxless stack, ensuring your SaaS is accessible to the world with minimal fuss.

Option 1: Deploy on a Digital Ocean Droplet

  1. Create a Droplet:

    • Log into Digital Ocean and create a Droplet (e.g., Ubuntu 22.04, 4GB RAM, 2 vCPUs for small apps).
    • Enable SSH access and add your public key for secure access.
  2. Install Docker and Docker Compose:
    SSH into your Droplet and install Docker:

   sudo apt update
   sudo apt install -y docker.io docker-compose
   sudo systemctl start docker
   sudo systemctl enable docker
Enter fullscreen mode Exit fullscreen mode
  1. Clone and Configure Staxless: Clone the Staxless repo and copy your production environment variables to /config/secrets/prod/ (e.g., auth-service.env, stripe-service.env from /config/secrets/prod/*.env.template):
   git clone https://github.com/staxless/staxless.git
   cd staxless
   cp config/secrets/dev/staxless.dev.env config/secrets/prod/staxless.prod.env
Enter fullscreen mode Exit fullscreen mode

Edit staxless.prod.env with your production credentials (e.g., MongoDB Atlas URI, Mailgun API key, Stripe secret key). Example:

   # /config/secrets/prod/staxless.prod.env
   NODE_ENV=production
   DATABASE_URI=mongodb+srv://user:pass@cluster0.mongodb.net/database
   MAILGUN_API_KEY=your-mailgun-api-key
   STRIPE_PRIVATE_KEY=your-stripe-private-key
Enter fullscreen mode Exit fullscreen mode
  1. Update Docker Compose for Production: Modify /config/docker-compose.prod.yml to expose only necessary ports (e.g., 8000 for Kong) and set NODE_ENV=production. Example change:
   services:
     frontend:
       ports:
         - "80:3000" # Map port 80 to frontend
Enter fullscreen mode Exit fullscreen mode
  1. Deploy the Stack: Start the production stack:
   docker-compose -f config/docker-compose.prod.yml up -d
Enter fullscreen mode Exit fullscreen mode

This builds and runs all services (frontend, auth, user, stripe, email, cms, Kafka, MongoDB) with production settings.

  1. Set Up a Domain and SSL:

    • Point your domain (e.g., connectsphere.app) to the Droplet’s IP via Digital Ocean’s DNS.
    • Install an SSL certificate using Let’s Encrypt:
     sudo apt install -y certbot python3-certbot-nginx
     sudo certbot --nginx -d connectsphere.app
    
  2. Test the Deployment:
    Verify services are live:

   curl https://connectsphere.app/auth/health
Enter fullscreen mode Exit fullscreen mode

Visit https://connectsphere.app to see your SaaS in action.

Option 2: Deploy on Digital Ocean App Platform

  1. Set Up App Platform:

    • In Digital Ocean, create a new App and select your Staxless repo from GitHub.
    • Choose the Docker app type and specify config/docker-compose.prod.yml as the compose file.
  2. Configure Environment Variables:
    Add your production env variables in the App Platform UI, matching /config/secrets/prod/staxless.prod.env.

  3. Deploy the App:

    • Deploy the app, selecting a region (e.g., New York) and a plan (e.g., Basic for small apps).
    • App Platform automatically builds and deploys your services, handling scaling and load balancing.
  4. Add a Domain and SSL:

    • Link your domain in the App Platform settings.
    • Enable automatic SSL via Digital Ocean’s built-in Let’s Encrypt integration.
  5. Monitor and Scale:
    Use App Platform’s dashboard to monitor performance and scale resources as your user base grows.

Post-Deployment

  • Run Tests: Use /config/test.sh to verify end-to-end functionality (e.g., magic link flow, Stripe webhooks) in production.
  • Set Up Monitoring: Add Digital Ocean’s monitoring tools or integrate a third-party service like New Relic.
  • Join the Community: Premium plan users can access the Staxless Discord forum for deployment tips and support.

Your SaaS is now live on Digital Ocean, ready to scale with your community. Need help? DM me @zach_benson817 on X!


Get Started with Staxless

Ready to scale your SaaS like ConnectSphere? Staxless’s Basic plan ($299) gets you the full codebase, while the Premium plan ($499 or 3x $170) adds access to our Discord community forum and exclusive code snippets. Join the #BuildInPublic crew, save weeks of dev time, and build a SaaS that grows with you.

Got feedback? DM me @zach_benson817 on X!

Join the Staxless Waitlist Now

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.