> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tinkerer Pattern

> Experiment on the AWS free tier with the Fjall Tinkerer pattern for learning and development.

## Overview

The Tinkerer pattern targets experimentation and learning while staying within AWS free tier limits. It runs ECS on a single EC2 instance with direct access (no Application Load Balancer), keeping costs as low as possible.

## What's Included

```bash theme={null}
fjall create app --type tinkerer
```

Creates:

* **ECS on EC2** (t4g.micro instance)
* **Direct access** (no Application Load Balancer)
* **RDS Instance** database (t4g.micro)
* **Basic networking** setup

## Architecture

```
┌─────────────────────┐
│  ECS on EC2         │
│  (t4g.micro)        │ Direct port mapping (no ALB)
│  Memory: 400 MiB    │
└────────┬────────────┘
         |
┌────────┴────────┐
│  RDS Instance   │ (t4g.micro, free tier eligible)
└─────────────────┘
```

## Generated Infrastructure

```typescript theme={null}
#!/usr/bin/env node

import {
  App,
  DatabaseFactory,
  ComputeFactory,
  getConfig,
} from "@fjall/components-infrastructure";

const appName = "experiment";
const app = App.getApp(appName);

app.addTags({
  "fjall:costAllocation:owner": "engineering",
});

const experimentStorage = app.addDatabase(
  DatabaseFactory.build("ExperimentStorage", {
    type: "Instance",
    databaseName: "ExperimentDatabase",
    instanceType: "t4g.micro", // Free tier eligible
    allocatedStorage: 20, // Free tier: up to 20 GB
  }),
);

app.addCompute(
  ComputeFactory.build("ExperimentCompute", {
    type: "ecs",
    ecrRepository: app.getDefaultContainerRegistry(),
    cluster: {
      directAccess: true, // No ALB, direct port mapping
    },
    services: [
      {
        name: "app",
        capacityProvider: "EC2",
        memoryLimitMiB: 400,
        containers: [
          {
            port: 3000,
            environment: {
              ENVIRONMENT: getConfig().environment,
              DATABASE_HOST: experimentStorage.getHostEndpoint(),
              DATABASE_PORT: `${experimentStorage.getHostPort()}`,
              DATABASE_NAME: experimentStorage.getDatabaseName(),
            },
            secretsImport: {
              DATABASE_PASSWORD: experimentStorage
                .getCredentials()
                .getImport("password"),
            },
          },
        ],
      },
    ],
  }),
);
```

## Free Tier Limits

| Service       | Free Tier Allowance     | Tinkerer Usage |
| ------------- | ----------------------- | -------------- |
| EC2           | 750 hrs/month t4g.micro | 720 hrs (24/7) |
| RDS           | 750 hrs/month t4g.micro | 720 hrs (24/7) |
| EBS           | 30 GB storage           | \~8 GB         |
| Data Transfer | 100 GB/month            | Varies         |

<Warning>
  Running 24/7 uses 720 hours, staying within the 750-hour limit with buffer for restarts.
</Warning>

## Specifications

### Compute (ECS on EC2)

* **Instance type**: t4g.micro (ARM-based, free tier eligible)
* **Memory**: 400 MiB allocated to the container
* **Access**: Direct port mapping (no Application Load Balancer)
* **Auto-scaling**: Disabled (single instance)
* **Availability**: 2 AZ

### Database (RDS Instance)

* **Instance**: t4g.micro
* **Engine**: PostgreSQL 17.5
* **Storage**: 20 GB gp2 (free tier max)
* **Backup**: 1-day retention
* **Multi-AZ**: Not available

### Networking

* **VPC**: Default configuration
* **Subnets**: Public and private
* **NAT**: None (public subnets only)
* **Security groups**: Basic rules

## Cost Breakdown

**Monthly cost: \$0** (within free tier)

Potential charges if exceeding limits:

* Extra EC2 hours: \~\$0.0084/hour (t4g.micro)
* Extra RDS hours: \~\$0.016/hour
* Data transfer over 100 GB: \$0.09/GB
* EBS storage over 30 GB: \$0.10/GB/month

## Use Cases

**Perfect for:**

* Learning AWS and Fjall
* Personal projects
* Proof of concepts
* Development environments
* Hackathon projects
* Portfolio applications

**Not suitable for:**

* Production workloads
* High-traffic applications
* Multi-AZ requirements
* Large databases
* CPU-intensive tasks

## Customisation Options

### Environment Variables

```typescript theme={null}
app.addCompute(
  ComputeFactory.build("ExperimentCompute", {
    type: "ecs",
    cluster: {
      directAccess: true,
    },
    services: [
      {
        name: "app",
        capacityProvider: "EC2",
        memoryLimitMiB: 400,
        containers: [
          {
            environment: {
              NODE_ENV: "development",
              API_KEY: "dev-key-123",
              FEATURE_FLAGS: "experimental",
            },
          },
        ],
      },
    ],
  }),
);
```

### Adding Lambda Functions

Lambda has a free tier of 1M requests per month:

```bash theme={null}
fjall add compute --app experiment --name Worker --type lambda
```

## Monitoring Your Usage

### CloudWatch Dashboard

Monitor free tier usage:

* EC2 instance hours
* RDS instance hours
* Data transfer
* Storage usage

## Scaling Beyond Free Tier

When ready to scale:

### Option 1: Upgrade in Place

```bash theme={null}
# Move to a Lightweight pattern with Fargate
fjall create app --name production --type lightweight
```

### Option 2: Create New App

```bash theme={null}
# Create a production app with the Standard pattern
fjall create app --name production --type standard
# Migrate data and switch traffic
```

## Development Tips

### Local Development

Use Docker Compose to mirror the setup locally:

```yaml theme={null}
version: "3.8"
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_HOST=db
      - DATABASE_PORT=5432
      - DATABASE_NAME=experiment
      - DATABASE_PASSWORD=localpass
    depends_on:
      - db

  db:
    image: postgres:17-alpine
    environment:
      - POSTGRES_DB=experiment
      - POSTGRES_PASSWORD=localpass
```

### Resource Constraints

The t4g.micro instance has limited resources:

* Develop locally
* Deploy only tested code
* Use minimal dependencies
* Optimise container size

## Common Issues

### Out of Memory

The container is limited to 400 MiB:

* Reduce container memory usage
* Optimise application code
* Remove unused dependencies
* Use lightweight base images

### Database Connection Limits

t4g.micro has limited connections:

* Use connection pooling
* Close idle connections
* Implement retry logic

## Best Practices

1. **Monitor usage daily** during development
2. **Set billing alerts** at $1, $5, \$10
3. **Stop resources** when not in use
4. **Use Lambda** for sporadic workloads
5. **Optimise images** to reduce storage
6. **Clean up** unused resources

## Breaking Out the Pattern

When you are ready to move beyond the Tinkerer defaults, you can decompose the pattern into individual factory calls for full control over each component.

See the individual factory documentation for details:

* [Compute Factory](/patterns/compute-factory) - Customise ECS or Lambda settings
* [Database Factory](/patterns/database-factory) - Configure RDS parameters
* [Storage Factory](/patterns/storage-factory) - Manage S3 buckets
* [Network Factory](/patterns/network-factory) - Custom VPC and networking

## Next Steps

<CardGroup cols={2}>
  <Card title="Deploy your application" icon="rocket" href="/deployment/deploy-application">
    Ship the Tinkerer stack to AWS.
  </Card>

  <Card title="Add resources" icon="plus" href="/deployment/add-resources">
    Extend the application with databases, compute, storage, and more.
  </Card>

  <Card title="Upgrade to Lightweight" icon="feather" href="/patterns/lightweight-pattern">
    Move to Fargate when you outgrow the single instance.
  </Card>

  <Card title="Upgrade to Standard" icon="building" href="/patterns/standard-pattern">
    Run a production-ready, multi-AZ stack.
  </Card>
</CardGroup>
