Skip to content

Full-Stack Setup Troubleshooting Guide

Full-Stack Setup Troubleshooting Guide

Last Updated: February 28, 2026 Covers: React Client + Rails API + Python SaaS templates Tested With: sscli 2.6.13+


Table of Contents

  1. Overview
  2. Quick Diagnostics
  3. Common Issues by Category
  4. Full-Stack Integration Issues
  5. Template-Specific Issues
  6. Best Practices
  7. Real Error Messages & Solutions

Overview

This guide documents real issues encountered during full-stack setup and integration testing. It covers the most common pitfalls when combining React, Rails, and Python templates in a Docker environment.

What This Guide Covers

  • βœ… Frontend-backend connectivity
  • βœ… CORS configuration
  • βœ… Authentication flows (JWT, Session)
  • βœ… Database setup and migrations
  • βœ… Docker networking and volumes
  • βœ… Environment variable management
  • βœ… Port conflicts and permissions

Prerequisites

Before troubleshooting:

Terminal window
# Verify Docker is running
docker --version && docker-compose --version
# Check available memory (should be 4GB+)
docker system info | grep "Total Memory"
# Verify sscli installation
sscli --version

Quick Diagnostics

Run these commands to quickly identify common issues:

Terminal window
# 1. Check all service health
docker-compose ps
# 2. View logs for all services
docker-compose logs --tail=50
# 3. Check specific service
docker-compose logs -f [service-name]
# 4. Verify network connectivity
docker-compose exec client curl http://api:3000/health
docker-compose exec api curl http://db:5432
# 5. Check environment variables
docker-compose exec api env | grep -i database
docker-compose exec client env | grep -i vite
# 6. Test database connection
docker-compose exec db psql -U postgres -c "SELECT 1;"

Common Issues by Category

πŸ”΄ BLOCKERS (Cannot Complete Setup)

[B-001] Database Does Not Exist on First Run

Symptoms:

FATAL: database "rails_api_development" does not exist
PG::ConnectionBad: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed

Root Cause: Rails/Python services start before the database container is fully initialized and ready to accept connections.

Solution 1: Add health checks to docker-compose.yml

services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
api:
depends_on:
db:
condition: service_healthy

Solution 2: Add database creation to entrypoint Create docker-entrypoint.sh in Rails template:

#!/bin/bash
set -e
# Wait for postgres
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$DATABASE_HOST" -U "$POSTGRES_USER" -c '\q'; do
echo "Postgres is unavailable - sleeping"
sleep 1
done
echo "Postgres is up - creating database"
bundle exec rails db:create db:migrate
exec "$@"

Quick Fix:

Terminal window
# Manually create and migrate database
docker-compose exec api bash
bundle exec rails db:create db:migrate
exit

Priority: P0 (Must fix)


[B-002] Docker Volume Permission Errors (Linux)

Symptoms:

Error: EACCES: permission denied, mkdir '/app/node_modules'
SQLite3::CantOpenException: unable to open database file

Root Cause: On Linux, Docker containers run as root by default, but mounted volumes retain host user permissions, causing conflicts.

Solution 1: Add user mapping to docker-compose.yml

services:
client:
user: "${UID}:${GID}"
environment:
- HOME=/tmp

Solution 2: Fix permissions in Dockerfile

RUN mkdir -p /app/node_modules && \
chown -R node:node /app
USER node

Solution 3: Use named volumes instead of bind mounts

Under the service definition:

volumes:
- node_modules:/app/node_modules

At the top-level of docker-compose.yml:

volumes:
node_modules:

Quick Fix:

Terminal window
# On Linux host, set environment variables
export UID=$(id -u)
export GID=$(id -g)
docker-compose up

Priority: P0 (Blocker on Linux)


🟠 MAJOR (Significant Workaround Required)

[M-001] CORS Not Configured by Default

Symptoms:

Access to fetch at 'http://localhost:3000/api/v1/users' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the
requested resource.

Root Cause: Rails API template does not include CORS configuration by default. React client cannot make API calls.

Solution for Rails:

  1. Add to Gemfile:
gem 'rack-cors'
  1. Create config/initializers/cors.rb:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ENV.fetch('CORS_ORIGINS', 'http://localhost:5173').split(',')
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true,
max_age: 86400
end
end
  1. Add to .env:
Terminal window
CORS_ORIGINS=http://localhost:5173,http://localhost:3001
  1. Rebuild and restart:
Terminal window
docker-compose build api
docker-compose restart api

Solution for Python FastAPI:

In src/infrastructure/api/app.py:

from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=os.getenv("CORS_ORIGINS", "http://localhost:5173").split(","),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

Testing:

Terminal window
# From browser console
fetch('http://localhost:3000/api/v1/health')
.then(r => r.json())
.then(console.log)

Priority: P1 (Critical for frontend-backend integration)


[M-002] JWT Secret Not Configurable via Environment

Symptoms:

JWT::DecodeError: Signature verification failed
NoMethodError: undefined method `[]' for nil:NilClass (accessing ENV['JWT_SECRET'])

Root Cause: JWT authentication hardcodes the secret or doesn’t read from environment variables.

Solution for Rails (Devise + JWT):

  1. In config/initializers/devise.rb:
config.jwt do |jwt|
jwt.secret = ENV.fetch('JWT_SECRET') { Rails.application.credentials.secret_key_base }
jwt.dispatch_requests = [
['POST', %r{^/api/v1/auth/login$}]
]
jwt.revocation_requests = [
['DELETE', %r{^/api/v1/auth/logout$}]
]
jwt.expiration_time = 24.hours.to_i
end
  1. Generate secret:
Terminal window
openssl rand -hex 32
  1. Add to .env:
Terminal window
JWT_SECRET=your_64_character_hex_string_here

Solution for Python:

src/infrastructure/auth/jwt_manager.py
import os
from datetime import datetime, timedelta
from jose import jwt, JWTError
SECRET_KEY = os.getenv("JWT_SECRET")
if not SECRET_KEY:
raise ValueError("JWT_SECRET environment variable not set")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 1440 # 24 hours
def create_access_token(data: dict) -> str:
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

Priority: P1 (Security risk)


[M-003] Missing .env.example Files

Symptoms:

Configuration Error: Required environment variable 'DATABASE_URL' not found
User confusion: "What environment variables do I need?"

Root Cause: Templates don’t include .env.example files, forcing users to guess required configuration.

Solution:

Create .env.example in each template root:

Rails API:

Terminal window
# .env.example for rails-api
# Server Configuration
RAILS_ENV=development
PORT=3000
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rails_api_development
# Redis (optional)
REDIS_URL=redis://localhost:6379/1
# Authentication
SECRET_KEY_BASE=generate_with_rails_secret
JWT_SECRET=generate_with_openssl_rand_hex_32
# CORS
CORS_ORIGINS=http://localhost:5173,http://localhost:3001
# External Services (optional)
STRIPE_API_KEY=sk_test_...
SENDGRID_API_KEY=SG...

React Client:

Terminal window
# .env.example for react-client
# API Configuration
VITE_BACKEND_URL=http://localhost:3000
VITE_API_ROOT=/api/v1
# Authentication
VITE_AUTH_TYPE=token
# Feature Flags
VITE_ENABLE_ANALYTICS=false
VITE_ENABLE_DEBUG=true
# External Services (optional)
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_...
VITE_SENTRY_DSN=https://...@sentry.io/...

Python SaaS:

Terminal window
# .env.example for python-saas
# Application
APP_ENV=development
APP_PORT=8000
LOG_LEVEL=INFO
# Database
DATABASE_URL=sqlite:///./dev.db
# DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
# Security
SECRET_KEY=change_this_to_a_secure_random_string
# External APIs (optional)
OPENAI_API_KEY=sk-...
STRIPE_API_KEY=sk_test_...

Usage:

Terminal window
# User copies and configures
cp .env.example .env
# Edit .env with real values

Priority: P1 (Quality of life)


[M-004] Token Refresh Not Implemented

Symptoms:

User gets logged out after token expires (24 hours)
API returns 401 Unauthorized on all requests
React app doesn't attempt to refresh token

Root Cause: Frontend doesn’t implement token refresh logic; requires manual re-login.

Solution:

Implement token refresh interceptor in React:

src/api/axios.ts
import axios from "axios";
const api = axios.create({
baseURL: import.meta.env.VITE_BACKEND_URL,
});
// Request interceptor to add token
api.interceptors.request.use((config) => {
const token = localStorage.getItem("accessToken");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor to handle token refresh
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// If 401 and we haven't tried refreshing yet
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = localStorage.getItem("refreshToken");
const response = await axios.post("/api/v1/auth/refresh", {
refresh_token: refreshToken,
});
const { access_token } = response.data;
localStorage.setItem("accessToken", access_token);
// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${access_token}`;
return api(originalRequest);
} catch (refreshError) {
// Refresh failed, log user out
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
export default api;

Backend (Rails) refresh endpoint:

app/controllers/api/v1/auth_controller.rb
class Api::V1::AuthController < ApplicationController
skip_before_action :authenticate_user!, only: [:login, :refresh]
def refresh
token = request.headers['Authorization']&.split(' ')&.last
payload = JWT.decode(token, nil, false).first rescue nil
if payload && payload['refresh']
user = User.find(payload['user_id'])
new_token = generate_access_token(user)
render json: { access_token: new_token }
else
render json: { error: 'Invalid refresh token' }, status: :unauthorized
end
end
end

Priority: P1 (User experience)


🟑 MINOR (Small Inconvenience)

[Mi-001] Flash of Protected Content Before Redirect

Symptoms: User briefly sees protected page content before being redirected to login.

Root Cause: Auth check happens after component renders.

Solution:

src/components/ProtectedRoute.tsx
import { Navigate } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { user, loading } = useAuth();
// Show loading state while checking auth
if (loading) {
return <div>Loading...</div>;
}
// Redirect unauthenticated users
if (!user) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}

Priority: P2 (Polish)


[Mi-002] No Health Check Endpoint

Symptoms: No easy way to verify API is running from browser.

Solution for Rails:

config/routes.rb
get '/health', to: proc { [200, {}, ['OK']] }
# Or with more detail:
class HealthController < ApplicationController
skip_before_action :authenticate_user!
def show
render json: {
status: 'ok',
timestamp: Time.current.iso8601,
database: database_status,
redis: redis_status
}
end
private
def database_status
ActiveRecord::Base.connection.execute('SELECT 1')
'connected'
rescue
'disconnected'
end
def redis_status
$redis.ping == 'PONG' ? 'connected' : 'disconnected'
rescue
'disconnected'
end
end

Priority: P2 (Nice to have)


Full-Stack Integration Issues

Docker Compose Networking

Issue: Services Can’t Communicate

Error:

Error: getaddrinfo ENOTFOUND api
curl: (6) Could not resolve host: db

Solution:

Ensure all services are in the same network:

docker-compose.yml
version: "3.8"
services:
db:
image: postgres:15
networks:
- app-network
redis:
image: redis:7-alpine
networks:
- app-network
api:
build: ./templates/rails-api
networks:
- app-network
depends_on:
- db
- redis
client:
build: ./templates/react-client
networks:
- app-network
depends_on:
- api
networks:
app-network:
driver: bridge

Container-to-container URLs:

  • Use service names as hostnames: http://api:3000, postgresql://db:5432
  • NOT: http://localhost:3000 (localhost inside container refers to itself)

Environment Variable Propagation

Issue: .env Variables Not Available in Container

Error:

ENV['DATABASE_URL'] returns nil inside container

Solutions:

Option 1: env_file in docker-compose.yml

services:
api:
build: ./rails-api
env_file:
- ./rails-api/.env

Option 2: Explicit environment mapping

services:
api:
build: ./rails-api
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}

Option 3: Pass through .env file

Terminal window
# In .env file at docker-compose level
DATABASE_URL=postgresql://postgres:postgres@db:5432/app_dev
# docker-compose.yml
services:
api:
environment:
DATABASE_URL: ${DATABASE_URL}

Debugging:

Terminal window
# Check what environment variables are set inside container
docker-compose exec api env | sort
# Check specific variable
docker-compose exec api bash -c 'echo $DATABASE_URL'

Port Mapping vs Internal Ports

Common Confusion

External (host) ports vs Internal (container) ports:

services:
api:
ports:
- "3000:3000" # HOST:CONTAINER

Container-to-container communication:

  • βœ… Use internal port: http://api:3000
  • ❌ Don’t use host port: http://localhost:3000

Browser-to-container communication:

  • βœ… Use host port: http://localhost:3000

React .env configuration:

Terminal window
# For development (browser-to-API)
VITE_BACKEND_URL=http://localhost:3000
# For SSR or container-to-container
VITE_BACKEND_URL=http://api:3000

Template-Specific Issues

Rails API

Issue: Existing Database Schema Conflicts

Error:

ActiveRecord::StatementInvalid: PG::DuplicateTable: ERROR: relation "users" already exists

Solution:

Terminal window
# Reset database completely
docker-compose exec api rails db:drop db:create db:migrate
# Or load schema directly
docker-compose exec api rails db:schema:load

Issue: Missing Master Key

Error:

ActiveSupport::MessageEncryptor::InvalidMessage
Rails credentials are not configured

Solution:

Terminal window
# Generate new credentials
docker-compose exec api bash
EDITOR=vim rails credentials:edit
# Save and exit
# Or use environment variables instead of credentials
export SECRET_KEY_BASE=$(rails secret)

React Client

Issue: Vite Dev Server Not Accessible from Host

Error:

Cannot access http://localhost:5173
curl: (7) Failed to connect to localhost port 5173

Solution:

Update vite.config.ts:

export default defineConfig({
server: {
host: "0.0.0.0", // Listen on all interfaces
port: 5173,
watch: {
usePolling: true, // Required for Docker on some systems
},
},
});

Update Dockerfile.dev:

EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

Issue: Hot Module Replacement Not Working in Docker

Solution:

Add to vite.config.ts:

export default defineConfig({
server: {
watch: {
usePolling: true,
interval: 1000,
},
hmr: {
host: "localhost",
port: 5173,
},
},
});

Python SaaS

Issue: SQLite Database Locked in Docker

Error:

sqlite3.OperationalError: database is locked

Solution:

Use PostgreSQL in Docker instead of SQLite:

.env
DATABASE_URL=postgresql://postgres:postgres@db:5432/python_saas_dev

Or configure SQLite for concurrent access:

src/infrastructure/database.py
from sqlalchemy import create_engine, event
from sqlalchemy.engine import Engine
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_conn, connection_record):
cursor = dbapi_conn.cursor()
cursor.execute("PRAGMA journal_mode=WAL")
cursor.execute("PRAGMA busy_timeout=5000")
cursor.close()

Best Practices

1. Environment Variable Checklist

  • All templates have .env.example files
  • .env is in .gitignore
  • Required variables are documented with examples
  • Secrets use strong generation methods (openssl rand -hex 32)
  • Frontend variables start with VITE_ or PUBLIC_

2. Docker Development Setup

  • Use health checks for dependencies (db, redis)
  • Use named volumes for persistence
  • Bind mount source code for hot reload
  • Named networks for service communication
  • Resource limits set appropriately

Example:

services:
api:
build: ./api
volumes:
- ./api:/app # Source code
- bundle:/bundle # Gems (named volume)
mem_limit: 512m
cpus: 0.5

3. CORS Configuration

  • CORS origins configurable via environment
  • Allow credentials for auth cookies/tokens
  • Development allows localhost on all ports
  • Production restricts to specific domains
  • Preflight (OPTIONS) requests handled

4. Authentication Best Practices

  • JWT secret is configurable and secure
  • Tokens have reasonable expiration (24 hours)
  • Refresh token mechanism implemented
  • Auth state persists across browser refresh
  • Logout clears all auth artifacts

5. Database Management

  • Migrations run automatically on startup
  • Seed data available for development
  • Database volumes persist data
  • Connection pooling configured
  • Health checks ping database

6. Testing Your Setup

Run this checklist after initial setup:

Terminal window
# 1. All services start without errors
docker-compose up -d
docker-compose ps # All should be "Up"
# 2. Database is accessible
docker-compose exec db psql -U postgres -c "SELECT 1;"
# 3. API health check passes
curl http://localhost:3000/health
# 4. Frontend loads in browser
open http://localhost:5173
# 5. Frontend can call backend
# (Open browser console and run):
fetch('http://localhost:3000/health').then(r => r.json()).then(console.log)
# 6. Authentication flow works
# (Test login through UI)
# 7. Protected routes redirect when not authenticated
# (Navigate to protected route while logged out)

Real Error Messages & Solutions

Error 1: Connection Refused

Error: connect ECONNREFUSED 127.0.0.1:3000

Diagnosis:

  • API service not running
  • Wrong URL (using localhost inside container)

Fix:

Terminal window
# Check if API is running
docker-compose ps api
# Check API logs
docker-compose logs api
# If using container-to-container, use service name
VITE_BACKEND_URL=http://api:3000 # not localhost

Error 2: CORS Preflight Failure

Access to XMLHttpRequest at 'http://localhost:3000/api/v1/users' from origin
'http://localhost:5173' has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: No 'Access-Control-Allow-Origin' header
is present on the requested resource.

Diagnosis: CORS middleware not configured or origin not allowed.

Fix: See M-001 CORS Configuration


Error 3: Invalid JWT Signature

JWT::DecodeError (Signature verification failed):
OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key:

Diagnosis:

  • JWT secret mismatch between frontend and backend
  • JWT secret changed after tokens were issued
  • Missing JWT_SECRET environment variable

Fix:

Terminal window
# Verify JWT_SECRET is set consistently
docker-compose exec api env | grep JWT_SECRET
# Regenerate JWT secret
openssl rand -hex 32
# Update in both frontend and backend .env
JWT_SECRET=your_new_secret
# Rebuild and restart
docker-compose build api
docker-compose restart api
# Users will need to log in again

Error 4: Database Migration Pending

ActiveRecord::PendingMigrationError
Migrations are pending. To resolve this issue, run:
bin/rails db:migrate RAILS_ENV=development

Diagnosis: New migrations exist that haven’t been run.

Fix:

Terminal window
# Run migrations
docker-compose exec api rails db:migrate
# Or add to docker-entrypoint.sh to run automatically
bundle exec rails db:migrate

Error 5: Module Not Found (Node)

Error: Cannot find module '@/components/Dashboard'

Diagnosis:

  • TypeScript path aliases not configured
  • Missing dependency
  • Case-sensitive import

Fix:

Check vite.config.ts:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});

Check tsconfig.json:

{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}

Error 6: Permission Denied (Linux)

Error: EACCES: permission denied, open '/app/package.json'

Diagnosis: Volume mounted files have wrong ownership on Linux.

Fix: See B-002 Docker Volume Permission Errors


Getting Help

If you’ve tried everything in this guide:

  1. Check System Status

    Terminal window
    docker-compose ps
    docker-compose logs --tail=100
  2. Gather Diagnostic Info

    Terminal window
    # Save to file
    docker-compose ps > diagnostic.txt
    docker-compose logs >> diagnostic.txt
    docker system info >> diagnostic.txt
    env | grep -i vite >> diagnostic.txt
  3. Submit Issue

  4. Use Feedback Command

    Terminal window
    sscli feedback bug

Appendix: Quick Reference

Port Reference

ServiceInternal PortExternal PortURL (Host)URL (Container)
React Client51735173http://localhost:5173http://client:5173
Rails API30003000http://localhost:3000http://api:3000
Python SaaS80008000http://localhost:8000http://python:8000
PostgreSQL54325432localhost:5432db:5432
Redis63796379localhost:6379redis:6379

Environment Variable Prefixes

TemplateRequired PrefixExample
React (Vite)VITE_VITE_API_URL
AstroPUBLIC_PUBLIC_API_URL
RailsNoneDATABASE_URL
PythonNoneDATABASE_URL

Common Commands

Terminal window
# Start everything
docker-compose up -d
# Restart single service
docker-compose restart api
# View logs (live)
docker-compose logs -f api
# Execute command in container
docker-compose exec api bash
# Rebuild after code change
docker-compose build api
docker-compose up -d api
# Nuclear option (reset everything)
docker-compose down -v
docker-compose up --build

Document Version: 1.0.0 Maintained By: Seed & Source Team Feedback: github.com/seed-source/foundry-meta/issues