Skip to main content

Installation

Aya supports two installation methods:
  1. Docker Compose (recommended) - containerized development with all dependencies included
  2. Nix (advanced) - native development with reproducible tooling

Prerequisites

Before installing Aya, ensure you have:
  • Docker 24.0+ (or OrbStack for macOS)
  • Git 2.30+
  • Make (usually pre-installed on Unix/macOS)
  • 4 GB RAM minimum, 8 GB recommended
  • 10 GB free disk space

Docker Compose Installation

The Docker Compose setup is a complete, isolated environment perfect for development and production deployments.
1

Clone the repository

git clone [email protected]:eser/aya.is.git
cd aya.is
2

Configure environment variables

Create the backend environment file:
cat > apps/services/.env.local << 'EOF'
# Authentication (REQUIRED)
AUTH__JWT_SECRET=replace-with-secure-random-string

# Optional: Telegram Bot
# TELEGRAM__ENABLED=true
# TELEGRAM__BOT_TOKEN=your-bot-token
# TELEGRAM__BOT_USERNAME=your_bot_username
# TELEGRAM__USE_POLLING=true
# WORKERS__TELEGRAM_BOT__ENABLED=true

# Optional: GitHub OAuth
# AUTH__GITHUB__CLIENT_ID=your-github-oauth-app-id
# AUTH__GITHUB__CLIENT_SECRET=your-github-oauth-secret

# Optional: Apple OAuth
# AUTH__APPLE__ENABLED=true
# AUTH__APPLE__CLIENT_ID=your-apple-client-id
# AUTH__APPLE__TEAM_ID=your-apple-team-id
# AUTH__APPLE__KEY_ID=your-apple-key-id
# AUTH__APPLE__PRIVATE_KEY=your-apple-private-key

# Optional: Email (Resend)
# RESEND__API_KEY=your-resend-api-key

# Optional: S3 Storage
# S3__ENDPOINT=https://s3.amazonaws.com
# S3__ACCESS_KEY_ID=your-access-key
# S3__SECRET_ACCESS_KEY=your-secret
# S3__BUCKET=your-bucket-name
# S3__REGION=us-east-1
EOF
Security: Generate a strong AUTH__JWT_SECRET for production:
openssl rand -hex 32
3

Build and start services

make up
This command:
  • Builds Docker images (first run takes 5-10 minutes)
  • Starts PostgreSQL with health checks
  • Runs database migrations automatically
  • Starts backend API on port 8080
  • Starts frontend dev server on port 3000
4

Verify installation

Check that all services are running:
docker compose ps
Expected output:
NAME                          STATUS          PORTS
aya-is-development-postgres-1    Up (healthy)    0.0.0.0:5432->5432/tcp
aya-is-development-services-1    Up              0.0.0.0:8080->8080/tcp
aya-is-development-webclient-1   Up              0.0.0.0:3000->3000/tcp
Test the application:
# Backend health check
curl http://localhost:8080/health

# Frontend (should return HTML)
curl http://localhost:3000/en

Docker Compose Configuration

The compose.yml file defines three services:
compose.yml
services:
  webclient:
    build:
      context: .
      dockerfile: apps/webclient/Dockerfile
      target: development
    environment:
      VITE_BACKEND_URI: http://localhost:8080
      VITE_HOST: http://localhost:3000
      BACKEND_URI: http://services:8080  # SSR uses internal network
    ports:
      - 3000:3000

  services:
    build:
      context: .
      dockerfile: apps/services/Dockerfile
      target: development-runner
    environment:
      ENV: development
      PORT: 8080
      CONN__targets__default__protocol: postgres
      CONN__targets__default__dsn: postgres://postgres:s3cr3t@postgres:5432/postgres?sslmode=disable
    ports:
      - 8080:8080
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16-bookworm
    environment:
      POSTGRES_PASSWORD: s3cr3t
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
Network Architecture: The frontend uses http://services:8080 for SSR (server-to-server) and http://localhost:8080 for client-side fetching.

Nix Installation

The Nix flake provides all development tools in a reproducible environment. Only PostgreSQL runs in Docker.
1

Install Nix with flakes

If you don’t have Nix installed:
# Install Nix (single-user)
sh <(curl -L https://nixos.org/nix/install) --no-daemon

# Enable flakes
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
For macOS, consider using Determinate Systems installer which enables flakes by default.
2

Clone the repository

git clone [email protected]:eser/aya.is.git
cd aya.is
3

Enter Nix development shell

nix develop
This installs and activates:
  • Go 1.25
  • Node.js 20
  • Deno
  • pnpm
  • PostgreSQL 16 client tools
  • sqlc, air, golangci-lint, govulncheck
  • go-mockery, gofumpt, gopls, gotools
  • betteralign, gcov2lcov, pre-commit
Optional: Use direnv to automatically activate the shell when you cd into the project:
echo "use flake" > .envrc
direnv allow
4

Configure environment variables

cat > apps/services/.env.local << 'EOF'
AUTH__JWT_SECRET=your-secret-key-here
EOF
The default config.json already points to localhost:5432 for the database.
5

Start PostgreSQL

docker compose up -d postgres
Wait for the health check to pass:
docker compose ps postgres
# Should show "Up (healthy)"
6

Run migrations

cd apps/services
make migrate
7

Start backend and frontend

Open three terminal windows (all within nix develop shell):
cd apps/services
make dev
This starts the Go server with Air hot reload on port 8080.

Nix Flake Structure

The flake.nix defines the development environment:
flake.nix
{
  description = "aya.is development environment";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs { inherit system; };
        go = pkgs.go_1_25;
        nodejs = pkgs.nodejs_20;
      in {
        devShells.default = pkgs.mkShell {
          packages = [
            go nodejs pkgs.deno pkgs.pnpm
            pkgs.git pkgs.gnumake
            pkgs.docker pkgs.docker-compose
            pkgs.postgresql_16 pkgs.sqlc pkgs.air
            pkgs.golangci-lint pkgs.govulncheck
            pkgs.go-mockery_2 pkgs.gofumpt
            pkgs.gopls pkgs.gotools
            pkgs.betteralign pkgs.gcov2lcov
            pkgs.pre-commit
          ];

          shellHook = ''pre-commit install --install-hooks > /dev/null 2>&1'';
        };
      }
    );
}

Environment Variables Reference

Aya uses a hierarchical configuration system with environment variables taking precedence over config.json.

Backend Configuration

All backend environment variables use double underscore (__) as a separator for nested keys.
# JWT Secret (REQUIRED)
AUTH__JWT_SECRET=your-secret-here

# GitHub OAuth
AUTH__GITHUB__CLIENT_ID=your-github-app-id
AUTH__GITHUB__CLIENT_SECRET=your-github-secret

# Apple OAuth
AUTH__APPLE__ENABLED=true
AUTH__APPLE__CLIENT_ID=your-apple-client-id
AUTH__APPLE__TEAM_ID=your-team-id
AUTH__APPLE__KEY_ID=your-key-id
AUTH__APPLE__PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"

Frontend Configuration

Frontend uses VITE_ prefix for variables bundled at build time:
.env (frontend)
# Backend API URL (for client-side fetching)
VITE_BACKEND_URI=http://localhost:8080

# Frontend host
VITE_HOST=http://localhost:3000

# Telegram bot username (for deep links)
VITE_TELEGRAM_BOT_USERNAME=aya_is_bot

# Auth provider toggles
VITE_AUTH_GITHUB_ENABLED=true
VITE_AUTH_APPLE_ENABLED=false

# Upload URI validation
VITE_ALLOWED_URI_PREFIXES_STORIES=https://objects.aya.is/
VITE_ALLOWED_URI_PREFIXES_PROFILES=https://objects.aya.is/,https://avatars.githubusercontent.com/
Security: Never put secrets in VITE_ variables - they’re exposed in the client bundle!

Database Setup

Migrations

Database migrations are located in apps/services/etc/data/default/migrations/ and use goose.
# Auto-applied on container start, or manually:
cd apps/services
make migrate

# Check migration status
make migrate-status

# Rollback last migration
make migrate-down

Accessing PostgreSQL

# Via Docker
docker compose exec postgres psql -U postgres -d postgres

# Via local psql (if installed)
psql postgres://postgres:s3cr3t@localhost:5432/postgres

Schema Overview

Key tables from the initial migration:
CREATE TABLE "profile" (
  "id" CHAR(26) PRIMARY KEY,
  "slug" TEXT UNIQUE NOT NULL,
  "kind" TEXT NOT NULL,  -- 'individual', 'organization', 'product'
  "profile_picture_uri" TEXT,
  "pronouns" TEXT,
  "created_at" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE TABLE "profile_tx" (
  "profile_id" CHAR(26),
  "locale_code" CHAR(12),  -- e.g., 'en', 'tr', 'pt-PT'
  "title" TEXT NOT NULL,
  "description" TEXT NOT NULL,
  PRIMARY KEY ("profile_id", "locale_code")
);

CREATE TABLE "user" (
  "id" CHAR(26) PRIMARY KEY,
  "name" TEXT NOT NULL,
  "email" TEXT,
  "github_handle" TEXT,
  "individual_profile_id" CHAR(26) REFERENCES "profile"
);

CREATE TABLE "story" (
  "id" CHAR(26) PRIMARY KEY,
  "slug" TEXT UNIQUE NOT NULL,
  "author_profile_id" CHAR(26) REFERENCES "profile",
  "kind" TEXT NOT NULL,  -- 'article', 'news', 'event'
  "cover_picture_uri" TEXT,
  "published_at" TIMESTAMP WITH TIME ZONE
);

CREATE TABLE "story_tx" (
  "story_id" CHAR(26),
  "locale_code" CHAR(12),
  "title" TEXT NOT NULL,
  "summary" TEXT,
  "content" TEXT NOT NULL,  -- Markdown/MDX
  PRIMARY KEY ("story_id", "locale_code")
);

Verification & Testing

After installation, run the full test suite:
make ok
This command runs:
  • Backend: golangci-lint, Go tests, coverage
  • Frontend: deno lint, deno fmt --check, deno task test:ci
Set up a pre-commit hook to run checks automatically:
pre-commit install

Troubleshooting

Clean up Docker resources:
docker system prune -a --volumes
Update flake lock file:
nix flake update
nix develop --refresh
Check if port 5432 is already in use:
lsof -i :5432
# Kill the process or change the port in compose.yml
Ensure environment variables are set correctly:
# For Docker
docker compose exec webclient env | grep VITE

# Should show:
# VITE_BACKEND_URI=http://localhost:8080
# BACKEND_URI=http://services:8080

Next Steps

Architecture

Learn about hexagonal architecture and system design

Development Setup

Configure your IDE and development workflow

Frontend Guide

Start building with Deno, TanStack Start, and React

Backend Guide

Explore Go business logic and API development

Build docs developers (and LLMs) love