# Dev Container Guide

Comprehensive documentation for the Flyer Crawler development container.

**Last Updated**: 2026-01-22

---

## Table of Contents

1. [Overview](#overview)
2. [Architecture](#architecture)
3. [PM2 Process Management](#pm2-process-management)
4. [Log Aggregation](#log-aggregation)
5. [Quick Reference](#quick-reference)
6. [Troubleshooting](#troubleshooting)

---

## Overview

The dev container provides a production-like environment for local development. Key features:

- **PM2 Process Management** - Matches production architecture (ADR-014)
- **Logstash Integration** - All logs forwarded to Bugsink (ADR-050)
- **HTTPS by Default** - Self-signed certificates via mkcert
- **Hot Reloading** - tsx watch mode for API and worker processes

### Container Services

| Service     | Container Name           | Purpose                        |
| ----------- | ------------------------ | ------------------------------ |
| Application | `flyer-crawler-dev`      | Node.js app, Bugsink, Logstash |
| PostgreSQL  | `flyer-crawler-postgres` | Primary database with PostGIS  |
| Redis       | `flyer-crawler-redis`    | Cache and job queue backing    |

### Access Points

| Service     | URL                      | Notes                     |
| ----------- | ------------------------ | ------------------------- |
| Frontend    | `https://localhost`      | NGINX proxies Vite (5173) |
| Backend API | `http://localhost:3001`  | Express server            |
| Bugsink     | `https://localhost:8443` | Error tracking UI         |
| PostgreSQL  | `localhost:5432`         | Direct database access    |
| Redis       | `localhost:6379`         | Direct Redis access       |

---

## Architecture

### Production vs Development Parity

The dev container was updated to match production architecture (ADR-014):

| Component    | Production             | Dev Container (OLD)    | Dev Container (NEW)     |
| ------------ | ---------------------- | ---------------------- | ----------------------- |
| API Server   | PM2 cluster mode       | `npm run dev` (inline) | PM2 fork + tsx watch    |
| Worker       | PM2 process            | Inline with API        | PM2 process + tsx watch |
| Frontend     | Static files via NGINX | Vite standalone        | PM2 + Vite dev server   |
| Logs         | PM2 logs -> Logstash   | Console only           | PM2 logs -> Logstash    |
| Process Mgmt | PM2                    | None                   | PM2                     |

### Container Startup Flow

When the container starts (`scripts/dev-entrypoint.sh`):

1. **NGINX** starts (HTTPS proxy for Vite and Bugsink)
2. **Bugsink** starts (error tracking on port 8000)
3. **Logstash** starts (log aggregation)
4. **PM2** starts with `ecosystem.dev.config.cjs`:
   - `flyer-crawler-api-dev` - API server
   - `flyer-crawler-worker-dev` - Background worker
   - `flyer-crawler-vite-dev` - Vite dev server
5. Container tails PM2 logs to stay alive

### Key Configuration Files

| File                           | Purpose                            |
| ------------------------------ | ---------------------------------- |
| `ecosystem.dev.config.cjs`     | PM2 development configuration      |
| `ecosystem.config.cjs`         | PM2 production configuration       |
| `scripts/dev-entrypoint.sh`    | Container startup script           |
| `docker/logstash/bugsink.conf` | Logstash pipeline configuration    |
| `docker/nginx/dev.conf`        | NGINX development configuration    |
| `compose.dev.yml`              | Docker Compose service definitions |
| `Dockerfile.dev`               | Container image definition         |

---

## PM2 Process Management

### Process Overview

PM2 manages three processes in the dev container:

```text
+--------------------+     +------------------------+     +--------------------+
|  flyer-crawler-    |     |  flyer-crawler-        |     |  flyer-crawler-    |
|  api-dev           |     |  worker-dev            |     |  vite-dev          |
+--------------------+     +------------------------+     +--------------------+
| tsx watch          |     | tsx watch              |     | vite --host        |
| server.ts          |     | src/services/worker.ts |     |                    |
| Port: 3001         |     | No port                |     | Port: 5173         |
+--------------------+     +------------------------+     +--------------------+
         |                          |                              |
         v                          v                              v
+------------------------------------------------------------------------+
|                        /var/log/pm2/*.log                               |
|                     (Logstash picks up for Bugsink)                     |
+------------------------------------------------------------------------+
```

### PM2 Commands

All commands should be run inside the container:

```bash
# View process status
podman exec -it flyer-crawler-dev pm2 status

# View all logs (tail -f style)
podman exec -it flyer-crawler-dev pm2 logs

# View specific process logs
podman exec -it flyer-crawler-dev pm2 logs flyer-crawler-api-dev

# Restart all processes
podman exec -it flyer-crawler-dev pm2 restart all

# Restart specific process
podman exec -it flyer-crawler-dev pm2 restart flyer-crawler-api-dev

# Stop all processes
podman exec -it flyer-crawler-dev pm2 delete all

# Show detailed process info
podman exec -it flyer-crawler-dev pm2 show flyer-crawler-api-dev

# Monitor processes in real-time
podman exec -it flyer-crawler-dev pm2 monit
```

### PM2 Log Locations

| Process                    | stdout Log                    | stderr Log                      |
| -------------------------- | ----------------------------- | ------------------------------- |
| `flyer-crawler-api-dev`    | `/var/log/pm2/api-out.log`    | `/var/log/pm2/api-error.log`    |
| `flyer-crawler-worker-dev` | `/var/log/pm2/worker-out.log` | `/var/log/pm2/worker-error.log` |
| `flyer-crawler-vite-dev`   | `/var/log/pm2/vite-out.log`   | `/var/log/pm2/vite-error.log`   |

### NPM Scripts for PM2

The following npm scripts are available for PM2 management:

```bash
# Start PM2 with dev config (inside container)
npm run dev:pm2

# Restart all PM2 processes
npm run dev:pm2:restart

# Stop all PM2 processes
npm run dev:pm2:stop

# View PM2 status
npm run dev:pm2:status

# View PM2 logs
npm run dev:pm2:logs
```

---

## Log Aggregation

### Log Flow Architecture (ADR-050)

All application logs flow through Logstash to Bugsink using a 3-project architecture:

```text
+------------------+     +------------------+     +------------------+
|   PM2 Logs       |     |   PostgreSQL     |     |   Redis/NGINX    |
|  /var/log/pm2/   |     | /var/log/        |     | /var/log/redis/  |
| (API + Worker)   |     |  postgresql/     |     | /var/log/nginx/  |
+--------+---------+     +--------+---------+     +--------+---------+
         |                        |                        |
         v                        v                        v
+------------------------------------------------------------------------+
|                              LOGSTASH                                   |
|                    /etc/logstash/conf.d/bugsink.conf                   |
|                         (Routes by log type)                           |
+------------------------------------------------------------------------+
         |                        |                        |
         v                        v                        v
+------------------+     +------------------+     +------------------+
|  Backend API     |     | Frontend (Dev)   |     | Infrastructure   |
|  (Project 1)     |     | (Project 2)      |     | (Project 4)      |
|  - Pino errors   |     | - Browser SDK    |     | - Redis warnings |
|  - PostgreSQL    |     |   (not Logstash) |     | - NGINX errors   |
+------------------+     +------------------+     | - Vite errors    |
                                                  +------------------+
```

### Log Sources

| Source       | Log Path                          | Format     | Errors To Bugsink |
| ------------ | --------------------------------- | ---------- | ----------------- |
| API Server   | `/var/log/pm2/api-*.log`          | Pino JSON  | Yes               |
| Worker       | `/var/log/pm2/worker-*.log`       | Pino JSON  | Yes               |
| Vite         | `/var/log/pm2/vite-*.log`         | Plain text | Yes (if error)    |
| PostgreSQL   | `/var/log/postgresql/*.log`       | PostgreSQL | Yes (ERROR/FATAL) |
| Redis        | `/var/log/redis/redis-server.log` | Redis      | Yes (warnings)    |
| NGINX Access | `/var/log/nginx/access.log`       | Combined   | Yes (5xx only)    |
| NGINX Error  | `/var/log/nginx/error.log`        | NGINX      | Yes               |

### Viewing Logs

```bash
# View Logstash processed logs
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-dev cat /var/log/logstash/pm2-api-$(date +%Y-%m-%d).log
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-dev cat /var/log/logstash/redis-operational-$(date +%Y-%m-%d).log

# View raw Redis logs
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-redis cat /var/log/redis/redis-server.log

# Check Logstash status
podman exec flyer-crawler-dev curl -s localhost:9600/_node/stats/pipelines?pretty
```

### Bugsink Access

- **URL**: `https://localhost:8443`
- **Login**: `admin@localhost` / `admin`
- **Projects**:
  - Project 1: Backend API (Dev) - Pino app errors, PostgreSQL errors
  - Project 2: Frontend (Dev) - Browser errors via Sentry SDK
  - Project 4: Infrastructure (Dev) - Redis warnings, NGINX errors, Vite build errors

**Note**: Frontend DSN uses nginx proxy (`/bugsink-api/`) because browsers cannot reach `localhost:8000` directly. See [BUGSINK-SETUP.md](../tools/BUGSINK-SETUP.md#frontend-nginx-proxy) for details.

---

## Quick Reference

### Starting the Dev Container

```bash
# Start all services
podman-compose -f compose.dev.yml up -d

# View container logs
podman-compose -f compose.dev.yml logs -f

# Stop all services
podman-compose -f compose.dev.yml down
```

### Common Tasks

| Task                 | Command                                                                        |
| -------------------- | ------------------------------------------------------------------------------ |
| Run tests            | `podman exec -it flyer-crawler-dev npm test`                                   |
| Run type check       | `podman exec -it flyer-crawler-dev npm run type-check`                         |
| View PM2 status      | `podman exec -it flyer-crawler-dev pm2 status`                                 |
| View PM2 logs        | `podman exec -it flyer-crawler-dev pm2 logs`                                   |
| Restart API          | `podman exec -it flyer-crawler-dev pm2 restart flyer-crawler-api-dev`          |
| Access PostgreSQL    | `podman exec -it flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev` |
| Access Redis CLI     | `podman exec -it flyer-crawler-redis redis-cli`                                |
| Shell into container | `podman exec -it flyer-crawler-dev bash`                                       |

### Environment Variables

Key environment variables are set in `compose.dev.yml`:

| Variable          | Value                         | Purpose                     |
| ----------------- | ----------------------------- | --------------------------- |
| `TZ`              | `America/Los_Angeles`         | Timezone (PST) for all logs |
| `NODE_ENV`        | `development`                 | Environment mode            |
| `DB_HOST`         | `postgres`                    | PostgreSQL hostname         |
| `REDIS_URL`       | `redis://redis:6379`          | Redis connection URL        |
| `FRONTEND_URL`    | `https://localhost`           | CORS origin                 |
| `SENTRY_DSN`      | `http://...@127.0.0.1:8000/1` | Backend Bugsink DSN         |
| `VITE_SENTRY_DSN` | `http://...@127.0.0.1:8000/2` | Frontend Bugsink DSN        |

### Timezone Configuration

All dev container services are configured to use PST (America/Los_Angeles) timezone for consistent log timestamps:

| Service    | Configuration                                    | Notes                          |
| ---------- | ------------------------------------------------ | ------------------------------ |
| App        | `TZ=America/Los_Angeles` in compose.dev.yml      | Also set via dev-entrypoint.sh |
| PostgreSQL | `timezone` and `log_timezone` in postgres config | Logs timestamps in PST         |
| Redis      | `TZ=America/Los_Angeles` in compose.dev.yml      | Alpine uses TZ env var         |
| PM2        | `TZ` in ecosystem.dev.config.cjs                 | Pino timestamps use local time |

**Verifying Timezone**:

```bash
# Check container timezone
podman exec flyer-crawler-dev date

# Check PostgreSQL timezone
podman exec flyer-crawler-postgres psql -U postgres -c "SHOW timezone;"

# Check Redis log timestamps
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-redis cat /var/log/redis/redis-server.log | head -5
```

**Note**: If you need UTC timestamps for production compatibility, change `TZ=UTC` in compose.dev.yml and restart containers.

---

## Troubleshooting

### PM2 Process Not Starting

**Symptom**: `pm2 status` shows process as "errored" or "stopped"

**Debug**:

```bash
# Check process logs
podman exec -it flyer-crawler-dev pm2 logs flyer-crawler-api-dev --lines 50

# Check if port is in use
podman exec -it flyer-crawler-dev netstat -tlnp | grep 3001

# Try manual start to see errors
podman exec -it flyer-crawler-dev tsx server.ts
```

**Common Causes**:

- Port already in use
- Missing environment variables
- Syntax error in code

### Logs Not Appearing in Bugsink

**Symptom**: Errors in application but nothing in Bugsink UI

**Debug**:

```bash
# Check Logstash is running
podman exec flyer-crawler-dev curl -s localhost:9600/_node/stats/pipelines?pretty

# Check Logstash logs for errors
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-dev cat /var/log/logstash/logstash.log

# Verify PM2 logs exist
podman exec flyer-crawler-dev ls -la /var/log/pm2/
```

**Common Causes**:

- Logstash not started
- Log file permissions
- Bugsink not running

### Redis Logs Not Captured

**Symptom**: Redis warnings not appearing in Bugsink

**Debug**:

```bash
# Verify Redis logs exist
MSYS_NO_PATHCONV=1 podman exec flyer-crawler-redis cat /var/log/redis/redis-server.log

# Verify shared volume is mounted
podman exec flyer-crawler-dev ls -la /var/log/redis/
```

**Common Causes**:

- `redis_logs` volume not mounted
- Redis not configured to write to file

### Hot Reload Not Working

**Symptom**: Code changes not reflected in running application

**Debug**:

```bash
# Check tsx watch is running
podman exec -it flyer-crawler-dev pm2 logs flyer-crawler-api-dev

# Manually restart process
podman exec -it flyer-crawler-dev pm2 restart flyer-crawler-api-dev
```

**Common Causes**:

- File watcher limit reached
- Volume mount issues on Windows

---

## Related Documentation

- [QUICKSTART.md](../getting-started/QUICKSTART.md) - Getting started guide
- [DEBUGGING.md](DEBUGGING.md) - Debugging strategies
- [LOGSTASH-QUICK-REF.md](../operations/LOGSTASH-QUICK-REF.md) - Logstash quick reference
- [DEV-CONTAINER-BUGSINK.md](../DEV-CONTAINER-BUGSINK.md) - Bugsink setup in dev container
- [ADR-014](../adr/0014-containerization-and-deployment-strategy.md) - Containerization and deployment strategy
- [ADR-050](../adr/0050-postgresql-function-observability.md) - PostgreSQL function observability (includes log aggregation)
