Claude-code-plugins-plus clickhouse-multi-env-setup
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/clickhouse-pack/skills/clickhouse-multi-env-setup" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-clickhouse-multi-env-setup && rm -rf "$T"
manifest:
plugins/saas-packs/clickhouse-pack/skills/clickhouse-multi-env-setup/SKILL.mdsource content
ClickHouse Multi-Environment Setup
Overview
Configure separate ClickHouse instances for development, staging, and production with proper secrets management, environment detection, and infrastructure-as-code.
Prerequisites
- ClickHouse Cloud account or self-hosted instances per environment
- Secret management solution (Vault, AWS Secrets Manager, GCP Secret Manager)
- CI/CD pipeline with environment variables
Instructions
Step 1: Environment Strategy
| Environment | Instance | Purpose | Data |
|---|---|---|---|
| Development | Docker local | Fast iteration | Synthetic seed data |
| Staging | ClickHouse Cloud (Dev tier) | Pre-prod validation | Sampled prod copy |
| Production | ClickHouse Cloud (Prod tier) | Live traffic | Real data |
Step 2: Configuration Module
// src/config/clickhouse.ts interface ClickHouseEnvConfig { url: string; username: string; password: string; database: string; maxConnections: number; requestTimeout: number; compression: boolean; } const configs: Record<string, ClickHouseEnvConfig> = { development: { url: 'http://localhost:8123', username: 'default', password: process.env.CLICKHOUSE_PASSWORD ?? 'dev_password', database: 'app_dev', maxConnections: 5, requestTimeout: 60_000, // Longer for debugging compression: false, // Easier to debug raw }, staging: { url: process.env.CLICKHOUSE_HOST ?? 'https://staging.clickhouse.cloud:8443', username: process.env.CLICKHOUSE_USER ?? 'app_staging', password: process.env.CLICKHOUSE_PASSWORD!, database: 'app_staging', maxConnections: 10, requestTimeout: 30_000, compression: true, }, production: { url: process.env.CLICKHOUSE_HOST!, username: process.env.CLICKHOUSE_USER!, password: process.env.CLICKHOUSE_PASSWORD!, database: 'app_prod', maxConnections: 20, requestTimeout: 30_000, compression: true, }, }; export function getConfig(): ClickHouseEnvConfig { const env = process.env.NODE_ENV ?? 'development'; const config = configs[env]; if (!config) throw new Error(`Unknown environment: ${env}`); // Validate required fields in non-dev environments if (env !== 'development') { if (!config.password) throw new Error(`CLICKHOUSE_PASSWORD not set for ${env}`); if (!config.url.startsWith('https://')) { throw new Error(`ClickHouse ${env} must use HTTPS`); } } return config; }
Step 3: Client Factory
// src/clickhouse/client.ts import { createClient, ClickHouseClient } from '@clickhouse/client'; import { getConfig } from '../config/clickhouse'; let client: ClickHouseClient | null = null; export function getClient(): ClickHouseClient { if (!client) { const config = getConfig(); client = createClient({ url: config.url, username: config.username, password: config.password, database: config.database, max_open_connections: config.maxConnections, request_timeout: config.requestTimeout, compression: { request: config.compression, response: config.compression, }, }); } return client; }
Step 4: Secrets Management
# --- Development --- # .env.local (git-ignored) CLICKHOUSE_PASSWORD=dev_password # --- Staging (GitHub Actions) --- # Set via: gh secret set CLICKHOUSE_PASSWORD_STAGING # Access in workflow: # env: # CLICKHOUSE_PASSWORD: ${{ secrets.CLICKHOUSE_PASSWORD_STAGING }} # --- Production (AWS Secrets Manager) --- aws secretsmanager create-secret \ --name clickhouse/production \ --secret-string '{"host":"https://prod.clickhouse.cloud:8443","password":"..."}' # Fetch at runtime: aws secretsmanager get-secret-value \ --secret-id clickhouse/production \ --query SecretString --output text # --- Production (GCP Secret Manager) --- echo -n "https://prod.clickhouse.cloud:8443" | \ gcloud secrets create ch-prod-host --data-file=- gcloud secrets versions access latest --secret=ch-prod-host # --- Production (HashiCorp Vault) --- vault kv put secret/clickhouse/prod \ host="https://prod.clickhouse.cloud:8443" \ password="..." vault kv get -field=password secret/clickhouse/prod
Step 5: Schema Management Across Environments
// scripts/apply-schema.ts import { getClient } from '../src/clickhouse/client'; import { readFileSync, readdirSync } from 'fs'; import { join } from 'path'; async function applySchema() { const client = getClient(); const env = process.env.NODE_ENV ?? 'development'; const schemaDir = join(__dirname, '../src/clickhouse/schemas'); const files = readdirSync(schemaDir).filter((f) => f.endsWith('.sql')).sort(); console.log(`Applying ${files.length} schema files to ${env}...`); for (const file of files) { const sql = readFileSync(join(schemaDir, file), 'utf-8'); try { await client.command({ query: sql }); console.log(` [OK] ${file}`); } catch (err) { console.error(` [FAIL] ${file}: ${(err as Error).message}`); if (env === 'production') throw err; // Fail hard in prod } } } applySchema();
Step 6: Environment Guards
// Prevent dangerous operations in production function requireNonProduction(operation: string): void { if (process.env.NODE_ENV === 'production') { throw new Error(`${operation} is blocked in production`); } } // TRUNCATE only in dev/staging async function resetTestData() { requireNonProduction('resetTestData'); const client = getClient(); await client.command({ query: 'TRUNCATE TABLE events' }); } // Prevent accidental cross-environment queries function validateDatabaseName(database: string): void { const env = process.env.NODE_ENV ?? 'development'; const expected = `app_${env === 'development' ? 'dev' : env}`; if (database !== expected) { throw new Error(`Database mismatch: expected ${expected}, got ${database}`); } }
Step 7: CI/CD Integration
# .github/workflows/deploy.yml jobs: deploy-staging: runs-on: ubuntu-latest environment: staging env: NODE_ENV: staging CLICKHOUSE_HOST: ${{ secrets.CLICKHOUSE_HOST_STAGING }} CLICKHOUSE_USER: ${{ secrets.CLICKHOUSE_USER_STAGING }} CLICKHOUSE_PASSWORD: ${{ secrets.CLICKHOUSE_PASSWORD_STAGING }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: "20" } - run: npm ci - run: npm run schema:apply # Apply schema changes - run: npm run test:integration # Run against staging CH - run: npm run deploy:staging deploy-production: needs: deploy-staging runs-on: ubuntu-latest environment: production # Requires manual approval env: NODE_ENV: production CLICKHOUSE_HOST: ${{ secrets.CLICKHOUSE_HOST_PROD }} CLICKHOUSE_USER: ${{ secrets.CLICKHOUSE_USER_PROD }} CLICKHOUSE_PASSWORD: ${{ secrets.CLICKHOUSE_PASSWORD_PROD }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: "20" } - run: npm ci - run: npm run schema:apply - run: npm run deploy:production
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Wrong database in prod | Env var not set | Validate config on startup |
| TLS error in staging | Using for Cloud | Force in non-dev |
| Schema drift | Applied manually | Use migration runner in CI |
| Secret not found | Missing env var | Check secret manager + CI config |
Resources
Next Steps
For monitoring and observability, see
clickhouse-observability.