Claude
Skills
Sign in
Back

coder-convex-setup

Included with Lifetime
$97 forever

Initial Convex workspace setup in Coder workspaces with self-hosted Convex deployment, authentication configuration, Docker setup, and environment variable generation

Cloud & DevOps

What this skill does


# Coder-Convex-Setup: Initial Convex Workspace Setup in Coder

You are an expert at **initial setup and configuration** of self-hosted Convex in Coder workspaces. This skill is ONLY for the one-time setup of a new Convex workspace. For everyday Convex development, use the `coder-convex` skill instead.

## When to Use This Skill

Use this skill when:
- Setting up Convex in a new Coder workspace for the first time
- Configuring a self-hosted Convex deployment
- Setting up Docker-based Convex backend
- Configuring environment variables for Convex
- Generating admin keys and deployment URLs

**DO NOT use this skill for:**
- Everyday Convex development (use `coder-convex` instead)
- Writing queries, mutations, or actions (use `coder-convex` instead)
- Schema modifications (use `coder-convex` instead)
- React integration issues (use `coder-convex` instead)

## Prerequisites

Before setting up Convex in a Coder workspace, ensure:

1. **Node.js and a package manager are installed**:
   ```bash
   node --version  # Should be v18+
   # Check for package manager: pnpm, yarn, npm, or bun
   pnpm --version  # Or: yarn --version, npm --version, bun --version
   ```

2. **Docker is available**:
   ```bash
   docker --version
   docker compose version
   ```

3. **Project has package.json with Convex dependency**:
   ```json
   {
     "dependencies": {
       "convex": "^1.31.3"
     }
   }
   ```

## Coder Workspace Services Overview

In a Coder workspace, Convex is exposed through multiple services. Understanding these is critical:

| Slug | Display Name | Internal URL | Port | Hidden | Purpose |
|------|-------------|--------------|------|--------|---------|
| `convex-dashboard` | Convex Dashboard | `localhost:6791` | 6791 | No | Admin dashboard |
| `convex-api` | Convex API | `localhost:3210` | 3210 | **Yes** | Main API endpoints |
| `convex-site` | Convex Site | `localhost:3211` | 3211 | **Yes** | **Site Proxy (Auth)** |

## Step 1: Install Convex Dependencies

```bash
# Install Convex package
[package-manager] add convex

# Install auth dependencies (required for Coder workspaces)
[package-manager] add @convex-dev/auth

# Install dev dependencies if not present
[package-manager] add -D @types/node typescript
```

## Step 2: Create Convex Directory Structure

Create the following directory structure:

```bash
mkdir -p convex/lib
```

The structure should look like:

```
convex/
├── lib/                  # Internal utilities (optional)
├── schema.ts            # Database schema (required)
├── auth.ts              # Auth setup (required for Coder)
├── router.ts            # HTTP routes (required for auth endpoints)
└── http.ts              # HTTP exports with auth routes (required for Coder)
```

## Step 3: Create Initial Schema

Create [convex/schema.ts](convex/schema.ts):

```typescript
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
import { authTables } from "@convex-dev/auth/server";

// Your application tables
const applicationTables = {
  // Add your tables here
  tasks: defineTable({
    title: v.string(),
    status: v.string(),
  }).index("by_status", ["status"]),
};

export default defineSchema({
  ...authTables,
  ...applicationTables,
});
```

**Key Schema Rules**:
- Always include `...authTables` from `@convex-dev/auth/server` for Coder workspaces
- Never manually add `_id` or `_creationTime` - they're automatic
- Index names should be descriptive: `by_fieldName`
- All indexes automatically include `_creationTime` as the last field
- Don't use `.index("by_creation_time", ["_creationTime"])` - it's built-in

## Step 4: Create Auth Configuration

> **Note**: Modern `@convex-dev/auth` (v0.0.90+) uses the `convexAuth()` function directly. A separate `auth.config.ts` file is no longer required.

Create [convex/auth.ts](convex/auth.ts):

```typescript
import { convexAuth, getAuthUserId } from "@convex-dev/auth/server";
import { Password } from "@convex-dev/auth/providers/Password";
import { Anonymous } from "@convex-dev/auth/providers/Anonymous";
import { query } from "./_generated/server";

// Configure auth with providers
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [Password, Anonymous],
});

// Query to get the current user
export const currentUser = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) {
      return null;
    }
    return await ctx.db.get(userId);
  },
});
```

Create [convex/router.ts](convex/router.ts):

```typescript
import { httpRouter } from "convex/server";

const http = httpRouter();

export default http;
```

Create [convex/http.ts](convex/http.ts):

```typescript
import { auth } from "./auth";
import router from "./router";

const http = router;

// CRITICAL: Add auth routes to the HTTP router
auth.addHttpRoutes(http);

export default http;
```

**Critical**: The `auth.addHttpRoutes(http)` call is required for auth endpoints (`/auth/*`) to be accessible. Without this, authentication will not work.

## Step 5: Create Coder Setup Script

Create [scripts/setup-convex.sh](scripts/setup-convex.sh):

```bash
#!/bin/bash

# Detect Coder workspace environment
# Check for both CODER and CODER_WORKSPACE_NAME to confirm we're in a Coder workspace
if [ -n "$CODER" ] && [ -n "$CODER_WORKSPACE_NAME" ]; then
  # Running in Coder workspace
  # Extract protocol and domain from CODER_URL (e.g., https://coder.hahomelabs.com)
  CODER_PROTOCOL="${CODER_URL%%://*}"
  CODER_DOMAIN="${CODER_URL#*//}"
  WORKSPACE_NAME="${CODER_WORKSPACE_NAME}"
  USERNAME="${CODER_WORKSPACE_OWNER_NAME:-$USER}"

  # Generate Coder-specific URLs
  # Format: <protocol>://<service>--<workspace>--<owner>.<coder-domain>
  CONVEX_API_URL="${CODER_PROTOCOL}://convex-api--${WORKSPACE_NAME}--${USERNAME}.${CODER_DOMAIN}"
  CONVEX_SITE_URL="${CODER_PROTOCOL}://convex-site--${WORKSPACE_NAME}--${USERNAME}.${CODER_DOMAIN}"
  CONVEX_DASHBOARD_URL="${CODER_PROTOCOL}://convex--${WORKSPACE_NAME}--${USERNAME}.${CODER_DOMAIN}"
else
  # Local development
  CONVEX_API_URL="http://localhost:3210"
  CONVEX_SITE_URL="http://localhost:3211"
  CONVEX_DASHBOARD_URL="http://localhost:6791"
fi

# Determine PostgreSQL URL from environment
# Priority order: DATABASE_URL → POSTGRES_URI → POSTGRES_URL
# Note: We strip the database name from the URL since Convex appends INSTANCE_NAME automatically
# E.g., "postgres://...:5432/app" becomes "postgres://...:5432"
_RAW_POSTGRES_URL="${DATABASE_URL:-${POSTGRES_URI:-${POSTGRES_URL:-}}}"
if [ -n "$_RAW_POSTGRES_URL" ]; then
  # Remove trailing database name (e.g., /app) if present
  _STRIPPED_URL="${_RAW_POSTGRES_URL%/[^/]*}"
  # For Coder PostgreSQL with self-signed certificates, use sslmode=disable
  # Note: Rust postgres crate may not accept sslmode parameter in URL, depends on version
  if [[ "$_STRIPPED_URL" == *"?"* ]]; then
    # URL already has query parameters
    POSTGRES_URL="${_STRIPPED_URL}&sslmode=disable"
  else
    # Add query parameters - use disable for self-signed certs
    POSTGRES_URL="${_STRIPPED_URL}?sslmode=disable"
  fi
else
  # Fallback to default for local development
  POSTGRES_URL="postgresql://convex:convex@localhost:5432/convex?sslmode=disable"
fi

# Verify PostgreSQL URL is configured
if [ -z "$POSTGRES_URL" ]; then
  echo "❌ POSTGRES_URL is not set"
  echo "   Please set DATABASE_URL or POSTGRES_URL in your environment"
  echo ""
  echo "   In Coder workspaces, these variables are automatically provided."
  echo "   For local development, ensure PostgreSQL is running and set the variable."
  exit 1
fi

# Admin key will be generated by the container on first start
# The container's generate_admin_key.sh script is the proper way to generate keys
# We'll retrieve it after the container starts
CONVEX_ADMIN_KEY="${CONVEX_ADMIN_KEY:-}"

# Generate JWT private key for auth (PKCS#8 format)
if [ ! -f jwt_private_key.pem ]; then
  openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2

Related in Cloud & DevOps