Fully configured with authentication, database, Docker, and developer experience tools
- Next.js 16 - The React framework for production
- React 19 - Latest React with new features and performance optimizations
- TypeScript - Type safety and enhanced developer experience
- Tailwind CSS v4 - Utility-first CSS framework
- Better Auth - Modern and secure authentication solution
- Drizzle ORM - Type-safe SQL ORM for TypeScript
- PostgreSQL - Robust and scalable database
- Docker & Docker Compose - Containerized development environment
- ESLint & Prettier - Code quality and formatting
- Husky & Lint-staged - Git hooks for automatic code checking
- Commitlint - Conventional commit messages
- Adminer - Database management UI
- Node.js >= 20.0.0
- pnpm >= 10.0.0
- Docker & Docker Compose
- Git
-
Clone the repository
git clone https://github.com/yourusername/maxam-haustechnik.git cd maxam-haustechnik -
Run automatic installation
./scripts/initialize.sh
The script automatically performs these steps:
- ✅ Installs all packages with pnpm
- ✅ Creates
.envfile from template - ✅ Generates secure authentication secret
- ✅ Starts PostgreSQL database in Docker
- ✅ Generates and applies database migrations
-
Start development server
pnpm run dev
The application is now available at:
- 🌐 Next.js App: http://localhost:3000
- 🗄️ Adminer (DB UI): http://localhost:8080
If you prefer manual installation:
-
Install dependencies
pnpm install
-
Configure environment variables
cp .env.sample .env
Generate a secure secret:
openssl rand -base64 32
Add the generated value to your
.envfile:BETTER_AUTH_SECRET=your-generated-secret
-
Start database
docker compose up -d postgres
-
Generate and apply migrations
pnpm run db:generate pnpm run db:migrate
-
Start development server
pnpm run dev
This template supports two deployment methods: manual deployment (clone repo on server) and automated deployment via GitHub Actions CI/CD.
- Docker & Docker Compose installed on server
- Domain name configured (for SSL profile)
- SSL certificates (for SSL profile, e.g., via Let's Encrypt)
The deployment script supports two NGINX profiles:
Use when you have a reverse proxy (Traefik, Caddy, Cloudflare Tunnel, etc.) handling SSL:
- ✅ HTTP only (proxied through external reverse proxy)
- ✅ No SSL configuration needed
- ✅ Ideal for microservices architecture
Use when NGINX directly manages SSL certificates:
- ✅ HTTPS with SSL/TLS (ports 80, 443)
- ✅ Automatic HTTP to HTTPS redirect
- ✅ Let's Encrypt ready
⚠️ Requires domain configuration (see SSL Configuration)
When using the ssl profile, you must replace all {{DOMAIN}} placeholders in nginx/nginx.ssl.conf with your actual domain before deploying:
sed -i 's|{{DOMAIN}}|example.com|g' nginx/nginx.ssl.confCommit the change so it's available for both manual and automated deployments.
The deployment script validates that no unresolved {{DOMAIN}} placeholders remain and will abort if any are found.
SSL certificates must be obtained via Certbot before deployment. The NGINX configuration expects certificates at /etc/letsencrypt/live/<domain>/.
Clone the full repository on the server and run the deployment script locally.
First time deployment:
git clone https://github.com/yourusername/your-repo.git
cd your-repo
./scripts/deploy.shThe deployment script will:
- ✅ Verify Docker Compose installation
- ✅ Initialize project (install packages, setup .env)
- ✅ Configure database connection for Docker network
- ✅ Prompt for NGINX profile selection
- ✅ For SSL: Validate that domain is configured
- ✅ Build and start all containers
./scripts/deploy.sh [OPTIONS]
Options:
--profile <internal|ssl> Specify NGINX profile
--rebuild Force rebuild of Docker images
--init Force reinitialization of projectExamples:
# Interactive deployment (recommended for first time)
./scripts/deploy.sh
# Deploy with specific profile
./scripts/deploy.sh --profile internal
./scripts/deploy.sh --profile ssl
# Force rebuild after code changes
./scripts/deploy.sh --profile internal --rebuild
# Reinitialize and deploy
./scripts/deploy.sh --profile ssl --init --rebuildgit pull origin main
./scripts/deploy.sh --profile internal --rebuildThe CI/CD pipeline automatically builds Docker images and can deploy them to your server via SSH.
Push to main/tag → Code Analysis → Docker Build & Push to GHCR → Deploy to Server
- On push to
mainor version tags (v*): Runs code analysis, builds the Docker image, pushes it to GHCR, and automatically deploys using default settings - On manual trigger (
workflow_dispatch): Same pipeline, but with customizable profile, tag, and build options
Auto-deploy only runs when DEPLOY_PATH is configured as a repository variable. Remove it to disable automatic deployments.
The default deploy settings are defined at the top of .github/workflows/cd.yml and can be easily changed:
env:
DEFAULT_PROFILE: internal # Change to 'ssl' if needed
DEFAULT_TAG: latestRepository Variables (Settings → Variables → Actions):
| Variable | Description | Example |
|---|---|---|
NEXT_PUBLIC_APP_URL |
Full application URL (inlined at build time) | https://example.com |
DEPLOY_PATH |
Deployment directory on the server | /opt/myapp |
Repository Secrets (Settings → Secrets → Actions):
| Secret | Description |
|---|---|
DEPLOY_SSH_HOST |
Server IP or hostname |
DEPLOY_SSH_USER |
SSH username |
DEPLOY_SSH_KEY |
Private SSH key for server access |
GITHUB_TOKENis provided automatically by GitHub Actions.
Before the first automated deployment, prepare the server:
-
Create the deployment directory and place a configured
.envfile in it:mkdir -p /opt/myapp cp .env.sample /opt/myapp/.env # Edit .env with production values (POSTGRES_*, BETTER_AUTH_SECRET, RESEND_API_KEY) # Set POSTGRES_HOST=postgres (container hostname)
-
Ensure Docker is installed on the server
-
Add the SSH public key to the server's
~/.ssh/authorized_keys
The workflow can also be triggered manually via GitHub UI (Actions → Continuous delivery → Run workflow) to override defaults:
| Input | Description | Default |
|---|---|---|
build |
Build a new image before deploying | true |
profile |
NGINX profile (internal or ssl) |
required |
tag |
Image tag to deploy | latest |
Deploy latest build with different profile:
- Trigger workflow with
build: true, select profile
Deploy a specific version without rebuilding:
- Trigger workflow with
build: false,tag: v1.0.0
Images pushed to GHCR are tagged automatically:
| Git Event | Image Tags |
|---|---|
Push to main |
main, latest, sha-abc1234 |
Tag v1.2.3 |
1.2.3, 1.2, 1, sha-abc1234 |
Stop production deployment:
# Interactive mode
./scripts/stop.sh
# Or with profile parameter
./scripts/stop.sh --profile internal
./scripts/stop.sh --profile sslView running containers:
docker compose -f docker-compose.prod.yml psView logs:
# All services
docker compose -f docker-compose.prod.yml logs -f
# Specific service
docker compose -f docker-compose.prod.yml logs -f nextjs
docker compose -f docker-compose.prod.yml logs -f nginx-internalInternal Profile (with external reverse proxy):
Internet → Reverse Proxy (SSL) → NGINX (HTTP) → Next.js (:3000)
↓
PostgreSQL (:5432)
SSL Profile (direct SSL):
Internet → NGINX (:80/:443 with SSL) → Next.js (:3000)
↓
PostgreSQL (:5432)
next-starter/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── api/auth/[...all]/ # Auth API route
│ │ ├── globals.css # Global styles
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # Homepage
│ ├── components/ui/ # Reusable UI components (shadcn/ui)
│ │ ├── alert.tsx # Alert component
│ │ ├── button.tsx # Button component
│ │ ├── card.tsx # Card component
│ │ ├── checkbox.tsx # Checkbox component
│ │ ├── field.tsx # Field component
│ │ ├── input.tsx # Input component
│ │ ├── input-group.tsx # Input group component
│ │ ├── label.tsx # Label component
│ │ ├── radio-group.tsx # Radio group component
│ │ ├── select.tsx # Select component
│ │ ├── separator.tsx # Separator component
│ │ ├── spinner.tsx # Spinner component
│ │ ├── switch.tsx # Switch component
│ │ └── textarea.tsx # Textarea component
│ ├── shared/form/ # Shared form system
│ │ ├── components/ # Form components
│ │ │ ├── fields/ # Form field components
│ │ │ ├── helpers/ # Form helper components
│ │ │ ├── error-alert.tsx # Error alert component
│ │ │ ├── form-field.tsx # Form field wrapper
│ │ │ └── submit-button.tsx # Submit button component
│ │ ├── context/ # Form context provider
│ │ ├── hooks/ # Form hooks (useAppForm, useFormField)
│ │ ├── lib/ # Form factory, types, utilities
│ │ └── index.ts # Public form API
│ ├── lib/ # Core libraries
│ │ ├── auth/ # Auth configuration
│ │ │ ├── auth-client.ts # Client-side auth
│ │ │ └── auth.ts # Server-side auth
│ │ ├── database/ # Database configuration
│ │ │ ├── schema/ # Drizzle schema definitions
│ │ │ └── index.ts # DB connection
│ │ └── utils.ts # Helper functions
│ └── env.ts # Environment variable validation
├── .github/ # GitHub Actions
│ ├── actions/ # Reusable composite actions
│ │ ├── code-analysis/ # Code analysis action
│ │ ├── deploy/ # Deployment action
│ │ └── docker-build-push/ # Docker build & push action
│ └── workflows/ # CI/CD workflows
│ ├── cd.yml # Continuous delivery
│ └── ci.yml # Continuous integration
├── scripts/ # Automation scripts
│ ├── initialize.sh # Local setup script
│ ├── deploy.sh # Production deployment script
│ └── stop.sh # Stop production containers
├── nginx/ # NGINX configurations
│ ├── nginx.internal.conf # Internal profile (behind proxy)
│ └── nginx.ssl.conf # SSL profile (direct SSL)
├── drizzle/ # Database migrations
├── .husky/ # Git hooks
├── docker-compose.yml # Development services
├── docker-compose.prod.yml # Production services
├── drizzle.config.ts # Drizzle ORM config
├── entrypoint.sh # Docker entrypoint script
├── migrate.mjs # Standalone migration runner
├── next.config.ts # Next.js config
├── next.Dockerfile # Next.js production image
└── components.json # shadcn/ui config
# Development Server
pnpm run dev # Start development server
# Code Quality
pnpm run lint # Run ESLint
pnpm run lint:fix # ESLint with auto-fix
pnpm run format:check # Check Prettier formatting
pnpm run format:write # Format code
pnpm run typecheck # TypeScript type checking
# Database
pnpm run db:push # Push schema to database (development)
pnpm run db:generate # Generate migrations
pnpm run db:migrate # Apply migrations
pnpm exec drizzle-kit studio # Open Drizzle Studio# Local Setup
./scripts/initialize.sh # Full setup with database
./scripts/initialize.sh --skip-db # Setup without database
# Production Deployment (manual, full repo clone)
./scripts/deploy.sh # Interactive deployment
./scripts/deploy.sh --profile internal # Deploy with internal profile
./scripts/deploy.sh --profile ssl # Deploy with SSL profile
./scripts/deploy.sh --rebuild # Force rebuild images
./scripts/deploy.sh --init # Force reinitialization
# Production Deployment (remote, used by CI/CD)
./scripts/deploy.sh --remote --profile internal --image ghcr.io/user/repo:latest
# Stop Production
./scripts/stop.sh # Interactive stop
./scripts/stop.sh --profile internal # Stop internal profile
./scripts/stop.sh --profile ssl # Stop SSL profileThe project uses Docker Compose for the development environment:
# Start all services
docker compose up -d
# Start database only
docker compose up -d postgres
# Stop services
docker compose down
# Remove services and volumes
docker compose down -v
# View logs
docker compose logs -f [service-name]
# Check container status
docker compose psAvailable Services:
| Service | Port | Description |
|---|---|---|
nextjs |
3000 | Next.js application (development) |
postgres |
5432 | PostgreSQL database |
adminer |
8080 | Database management UI |
Managed via deployment scripts (see Production Deployment)
Available Services:
| Service | Port | Profile | Description |
|---|---|---|---|
nextjs |
3000 | all | Next.js application (production) |
postgres |
5432 | all | PostgreSQL database |
nginx-internal |
6001 | internal | NGINX reverse proxy (HTTP only) |
nginx-ssl |
80, 443 | ssl | NGINX reverse proxy (with SSL) |
This project is licensed under the MIT License - see the LICENSE file for details.