Skip to content

Deploying Forgejo

Introduction

Forgejo is a self-hosted, community-driven Git forge focused on software freedom, collaboration, and transparency. As a soft fork of Gitea, Forgejo maintains compatibility while emphasizing community governance, open development, and long-term sustainability. Written in Go with a Vue.js frontend, Forgejo provides a lightweight yet feature-rich platform for hosting Git repositories, managing code, and coordinating development workflows.

Forgejo is known for:

  • Community Governance: Fully community-driven project with transparent decision-making and democratic processes
  • Software Freedom: Committed to free and open-source software principles with MIT license
  • Git Hosting: Complete repository management with branching, tagging, and advanced Git operations
  • Code Collaboration: Pull requests, code reviews, inline comments, and approval workflows
  • Issue Management: Built-in issue tracker with labels, milestones, assignees, and project boards
  • CI/CD Integration: Native Forgejo Actions (GitHub Actions compatible) and webhook support
  • Package Registry: Built-in support for Docker, npm, Maven, NuGet, PyPI, and more
  • Wiki & Documentation: Integrated wiki system for project documentation and knowledge base
  • Organizations & Teams: Multi-tiered permission system with fine-grained access control
  • Migration Tools: Import repositories from GitHub, GitLab, Bitbucket, and other Git forges
  • API & Webhooks: Comprehensive REST API and webhook system for automation
  • Multilingual: Available in 30+ languages with active translation community
  • Lightweight: Minimal resource requirements with excellent performance on modest hardware

Common use cases include private code hosting, team collaboration, open-source project management, DevOps pipelines, self-hosted CI/CD, package hosting, and decentralized software development.

This comprehensive guide walks you through deploying Forgejo on Klutch.sh using Docker, including database configuration, SSH access, persistent storage, environment variables, and production-ready best practices.

Why Deploy Forgejo on Klutch.sh?

  • Complete Control: Own your Git infrastructure without vendor lock-in
  • Simplified Infrastructure: No need to manage servers or complex configurations manually
  • Persistent Storage: Reliable volume storage for Git repositories, uploads, and data
  • Database Integration: Easy PostgreSQL or MySQL setup with persistent data storage
  • Automatic HTTPS: Secure connections out of the box for safe code operations
  • SSH Support: Enable Git over SSH for secure repository operations
  • Environment Variables: Secure configuration management without hardcoding credentials
  • Easy Scaling: Scale resources as your development team grows
  • Cost-Effective: Pay only for what you use with transparent pricing
  • Zero Downtime: Rolling deployments without disrupting development workflows

Prerequisites

Before you begin, ensure you have the following:


Installation and Setup

Step 1: Create Your Project Directory

First, create a new directory for your Forgejo deployment project:

Terminal window
mkdir forgejo-klutch
cd forgejo-klutch
git init

Step 2: Create the Dockerfile

Create a Dockerfile in your project root directory. This will define your Forgejo container configuration:

FROM codeberg.org/forgejo/forgejo:11
# Forgejo runs on port 3000 by default for HTTP
EXPOSE 3000
# Expose SSH port for Git operations over SSH
EXPOSE 22
# The official image includes:
# - Forgejo application (Go binary)
# - Git command-line tools
# - SSH server for Git operations
# - SQLite, PostgreSQL, and MySQL drivers
# - All required dependencies
# Data directory that should be mounted as a persistent volume
VOLUME /data
# The image uses the official Forgejo entrypoint
# Configuration can be provided via environment variables or app.ini

Note: The official Forgejo Docker image is production-ready and includes all necessary components. Version 11 refers to Forgejo v11 (the latest major version as of writing).

Step 3: Create Environment Variables Template

Create a .env.example file to document required environment variables:

Terminal window
# .env.example - Forgejo Configuration Template
# Application Settings
FORGEJO__server__DOMAIN=example-app.klutch.sh
FORGEJO__server__ROOT_URL=https://example-app.klutch.sh/
FORGEJO__server__HTTP_PORT=3000
FORGEJO__server__DISABLE_SSH=false
FORGEJO__server__SSH_PORT=22
FORGEJO__server__START_SSH_SERVER=true
# Database Configuration (SQLite - default)
FORGEJO__database__DB_TYPE=sqlite3
FORGEJO__database__PATH=/data/forgejo.db
# Database Configuration (PostgreSQL - recommended for production)
# FORGEJO__database__DB_TYPE=postgres
# FORGEJO__database__HOST=postgres-app.klutch.sh:8000
# FORGEJO__database__NAME=forgejo
# FORGEJO__database__USER=forgejo
# FORGEJO__database__PASSWD=your_secure_password
# Database Configuration (MySQL - alternative)
# FORGEJO__database__DB_TYPE=mysql
# FORGEJO__database__HOST=mysql-app.klutch.sh:8000
# FORGEJO__database__NAME=forgejo
# FORGEJO__database__USER=forgejo
# FORGEJO__database__PASSWD=your_secure_password
# Security Settings
FORGEJO__security__INSTALL_LOCK=false
FORGEJO__security__SECRET_KEY=
FORGEJO__security__INTERNAL_TOKEN=
# Service Settings
FORGEJO__service__DISABLE_REGISTRATION=false
FORGEJO__service__REQUIRE_SIGNIN_VIEW=false
FORGEJO__service__ENABLE_NOTIFY_MAIL=false
FORGEJO__service__DEFAULT_KEEP_EMAIL_PRIVATE=true
# Repository Settings
FORGEJO__repository__ROOT=/data/git/repositories
FORGEJO__repository__DEFAULT_BRANCH=main
FORGEJO__repository__ENABLE_PUSH_CREATE_USER=true
FORGEJO__repository__ENABLE_PUSH_CREATE_ORG=true
# Actions (CI/CD) Settings
FORGEJO__actions__ENABLED=true
FORGEJO__actions__DEFAULT_ACTIONS_URL=https://code.forgejo.org
# Mailer Settings (optional)
# FORGEJO__mailer__ENABLED=true
# FORGEJO__mailer__SMTP_ADDR=smtp.gmail.com
# FORGEJO__mailer__SMTP_PORT=587
# FORGEJO__mailer__FROM=noreply@example.com
# FORGEJO__mailer__USER=your-email@gmail.com
# FORGEJO__mailer__PASSWD=your-app-password
# Logging
FORGEJO__log__MODE=console
FORGEJO__log__LEVEL=Info
# Session Settings
FORGEJO__session__PROVIDER=memory
# For production with multiple instances, use database:
# FORGEJO__session__PROVIDER=db

Note: Forgejo uses double underscore (__) to separate configuration sections in environment variables. The format is FORGEJO__{section}__{key}.

Step 4: Create .gitignore

Create a .gitignore file to exclude sensitive and unnecessary files:

# Environment files
.env
*.env.local
# Forgejo data directory (if testing locally)
data/
forgejo-data/
# Docker volumes
volumes/
# Logs
*.log
logs/
# OS files
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo

Step 5: Test Locally (Optional)

Before deploying to Klutch.sh, you can test your Forgejo setup locally:

Terminal window
# Build the Docker image
docker build -t my-forgejo .
# Run with SQLite (simplest setup)
docker run -d \
--name forgejo-test \
-p 3000:3000 \
-p 2222:22 \
-v $(pwd)/forgejo-data:/data \
-e FORGEJO__server__DOMAIN=localhost \
-e FORGEJO__server__ROOT_URL=http://localhost:3000/ \
my-forgejo
# Access at http://localhost:3000
# Stop with: docker stop forgejo-test && docker rm forgejo-test

For local development with PostgreSQL, you can use Docker Compose:

version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: forgejo
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: forgejo
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
forgejo:
build: .
depends_on:
- postgres
ports:
- "3000:3000"
- "2222:22"
volumes:
- forgejo_data:/data
environment:
- FORGEJO__server__DOMAIN=localhost
- FORGEJO__server__ROOT_URL=http://localhost:3000/
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=postgres:5432
- FORGEJO__database__NAME=forgejo
- FORGEJO__database__USER=forgejo
- FORGEJO__database__PASSWD=forgejo
volumes:
postgres_data:
forgejo_data:

Start with docker-compose up -d and access at http://localhost:3000.

Step 6: Initialize Git Repository

Commit your files to Git:

Terminal window
# Add files
git add Dockerfile .env.example .gitignore
git commit -m "Initial Forgejo setup for Klutch.sh deployment"
# Create GitHub repository and push
git remote add origin https://github.com/yourusername/forgejo-klutch.git
git branch -M master
git push -u origin master

Deploying to Klutch.sh

Step 1: Push to GitHub

  1. Create a new repository on GitHub.
  2. Push your local repository:
Terminal window
git remote add origin https://github.com/yourusername/forgejo-klutch.git
git branch -M master
git push -u origin master

Step 2: Create a New App on Klutch.sh

  1. Log in to your Klutch.sh dashboard.
  2. Click New App and select your GitHub repository.
  3. Klutch.sh will automatically detect your Dockerfile and prepare for deployment.
  4. Select HTTP traffic since Forgejo is a web application.
  5. Set the internal port to 3000 (Forgejo's default HTTP port).

Step 3: Configure Environment Variables

In the Klutch.sh dashboard, add the following environment variables. Start with SQLite for simplicity, or configure PostgreSQL/MySQL for production.

Basic Configuration (SQLite):

Terminal window
# Application Settings
FORGEJO__server__DOMAIN=your-app.klutch.sh
FORGEJO__server__ROOT_URL=https://your-app.klutch.sh/
FORGEJO__server__HTTP_PORT=3000
# Database (SQLite)
FORGEJO__database__DB_TYPE=sqlite3
FORGEJO__database__PATH=/data/forgejo.db
# SSH Settings
FORGEJO__server__DISABLE_SSH=false
FORGEJO__server__SSH_PORT=22
FORGEJO__server__START_SSH_SERVER=true
# Repository Settings
FORGEJO__repository__ROOT=/data/git/repositories
FORGEJO__repository__DEFAULT_BRANCH=main
# Actions (CI/CD)
FORGEJO__actions__ENABLED=true

Production Configuration (PostgreSQL):

If using PostgreSQL (recommended for production), deploy PostgreSQL first (see our PostgreSQL guide), then configure:

Terminal window
# Application Settings
FORGEJO__server__DOMAIN=your-app.klutch.sh
FORGEJO__server__ROOT_URL=https://your-app.klutch.sh/
FORGEJO__server__HTTP_PORT=3000
# Database (PostgreSQL)
FORGEJO__database__DB_TYPE=postgres
FORGEJO__database__HOST=postgres-app.klutch.sh:8000
FORGEJO__database__NAME=forgejo
FORGEJO__database__USER=forgejo_user
FORGEJO__database__PASSWD=your_secure_password
# SSH Settings
FORGEJO__server__DISABLE_SSH=false
FORGEJO__server__SSH_PORT=22
FORGEJO__server__START_SSH_SERVER=true
# Security (will be auto-generated on first run if empty)
FORGEJO__security__SECRET_KEY=
FORGEJO__security__INTERNAL_TOKEN=
# Repository Settings
FORGEJO__repository__ROOT=/data/git/repositories
FORGEJO__repository__DEFAULT_BRANCH=main
# Actions
FORGEJO__actions__ENABLED=true
FORGEJO__actions__DEFAULT_ACTIONS_URL=https://code.forgejo.org
# Service Settings
FORGEJO__service__DISABLE_REGISTRATION=false
FORGEJO__service__REQUIRE_SIGNIN_VIEW=false

Mark sensitive variables like FORGEJO__database__PASSWD, FORGEJO__security__SECRET_KEY, and FORGEJO__security__INTERNAL_TOKEN as secret.

Step 4: Attach Persistent Volumes

Forgejo requires persistent storage for repositories, data, and configuration:

  1. In the Klutch.sh dashboard, navigate to Volumes.
  2. Add a persistent volume:

Primary Data Volume:

  • Mount Path: /data
  • Size: 20-100 GB (depending on expected repository size)
  • Purpose: Git repositories, database (if using SQLite), configuration, logs, and user uploads

Note: All Forgejo data is stored in /data, including:

  • /data/forgejo.db - SQLite database (if using SQLite)
  • /data/git/repositories - Git repository storage
  • /data/gitea/conf - Configuration files (app.ini)
  • /data/gitea/log - Application logs
  • /data/forgejo/attachments - Issue and pull request attachments
  • /data/gitea/indexers - Search indexes
  • /data/tmp - Temporary files

Step 5: Deploy Your Application

  1. Click Deploy in the Klutch.sh dashboard.
  2. Klutch.sh will build your Docker image and deploy the application.
  3. Monitor the build logs for any errors.
  4. Once deployed, your Forgejo instance will be accessible at https://your-app.klutch.sh.

Step 6: Complete Initial Setup

  1. Access your deployed Forgejo instance at https://your-app.klutch.sh.
  2. On first access, you'll see the installation wizard (if FORGEJO__security__INSTALL_LOCK=false).
  3. Review the pre-configured settings from environment variables.
  4. Create the initial administrator account with a strong password.
  5. Complete the installation to finalize setup.

After installation, the INSTALL_LOCK setting will be automatically set to true, preventing the installation wizard from running again.

Step 7: Configure SSH Access (Optional)

For Git operations over SSH (recommended for developers):

  1. SSH is enabled by default on port 22 inside the container.
  2. Git clone commands will use SSH format: git clone ssh://git@your-app.klutch.sh:2222/username/repo.git
  3. Add your SSH public key in Forgejo settings under Settings → SSH / GPG Keys.
  4. Test SSH access: ssh -p 2222 git@your-app.klutch.sh

Note: If Klutch.sh doesn’t support custom SSH ports externally, you can still use HTTPS for Git operations: git clone https://your-app.klutch.sh/username/repo.git


Configuration

Database Options

Forgejo supports three database backends:

SQLite (Default - Suitable for Small Teams):

Terminal window
FORGEJO__database__DB_TYPE=sqlite3
FORGEJO__database__PATH=/data/forgejo.db
  • Pros: Zero configuration, no external dependencies, easy backups
  • Cons: Limited concurrency, not suitable for high-traffic instances
  • Best for: Personal use, small teams (< 10 users), testing

PostgreSQL (Recommended for Production):

Terminal window
FORGEJO__database__DB_TYPE=postgres
FORGEJO__database__HOST=postgres-app.klutch.sh:8000
FORGEJO__database__NAME=forgejo
FORGEJO__database__USER=forgejo_user
FORGEJO__database__PASSWD=secure_password
  • Pros: Excellent performance, scalability, full-text search
  • Cons: Requires separate database instance
  • Best for: Medium to large teams, production deployments

MySQL/MariaDB (Alternative):

Terminal window
FORGEJO__database__DB_TYPE=mysql
FORGEJO__database__HOST=mysql-app.klutch.sh:8000
FORGEJO__database__NAME=forgejo
FORGEJO__database__USER=forgejo_user
FORGEJO__database__PASSWD=secure_password
  • Pros: Wide compatibility, familiar to many teams
  • Cons: Less feature-rich than PostgreSQL
  • Best for: Organizations with existing MySQL infrastructure

Email Configuration

Configure SMTP for notifications, password resets, and collaboration features:

Terminal window
# Enable email
FORGEJO__mailer__ENABLED=true
FORGEJO__mailer__PROTOCOL=smtp
# SMTP server settings
FORGEJO__mailer__SMTP_ADDR=smtp.gmail.com
FORGEJO__mailer__SMTP_PORT=587
FORGEJO__mailer__USE_STARTTLS=true
# Authentication
FORGEJO__mailer__USER=notifications@yourdomain.com
FORGEJO__mailer__PASSWD=your_app_password
# Email settings
FORGEJO__mailer__FROM=Forgejo <noreply@yourdomain.com>
FORGEJO__mailer__SKIP_VERIFY=false

Recommended SMTP Providers:

  • Gmail: Free for low volume (use app-specific password)
  • SendGrid: Excellent deliverability, generous free tier
  • Mailgun: Great for transactional emails
  • Amazon SES: Cost-effective for high volume

Git Operations Configuration

Fine-tune Git operations and repository settings:

Terminal window
# Repository defaults
FORGEJO__repository__ROOT=/data/git/repositories
FORGEJO__repository__DEFAULT_BRANCH=main
FORGEJO__repository__DEFAULT_PRIVATE=private
FORGEJO__repository__ENABLE_PUSH_CREATE_USER=true
FORGEJO__repository__ENABLE_PUSH_CREATE_ORG=true
# Git configuration
FORGEJO__git__DISABLE_DIFF_HIGHLIGHT=false
FORGEJO__git__MAX_GIT_DIFF_FILES=100
FORGEJO__git__MAX_GIT_DIFF_LINES=5000
FORGEJO__git__TIMEOUT__DEFAULT=360
FORGEJO__git__TIMEOUT__MIGRATE=600
FORGEJO__git__TIMEOUT__MIRROR=300
FORGEJO__git__TIMEOUT__CLONE=300
FORGEJO__git__TIMEOUT__PULL=300
FORGEJO__git__TIMEOUT__GC=60
# LFS (Large File Storage)
FORGEJO__lfs__START_SERVER=true
FORGEJO__lfs__STORAGE_TYPE=local
FORGEJO__lfs__PATH=/data/lfs

Forgejo Actions (CI/CD)

Configure Forgejo Actions for continuous integration and deployment:

Terminal window
# Enable Actions
FORGEJO__actions__ENABLED=true
# Default Actions URL (compatible with GitHub Actions)
FORGEJO__actions__DEFAULT_ACTIONS_URL=https://code.forgejo.org
# Or use GitHub for broader action availability
# FORGEJO__actions__DEFAULT_ACTIONS_URL=https://github.com
# Actions artifacts
FORGEJO__actions__ARTIFACTS_STORAGE_TYPE=local
FORGEJO__actions__ARTIFACTS_PATH=/data/actions_artifacts

To use Actions, you’ll need to deploy Forgejo Runners separately to execute workflows.

Security Settings

Enhance security with these configuration options:

Terminal window
# Security keys (auto-generated on first run if empty)
FORGEJO__security__INSTALL_LOCK=false
FORGEJO__security__SECRET_KEY=
FORGEJO__security__INTERNAL_TOKEN=
# Password requirements
FORGEJO__security__MIN_PASSWORD_LENGTH=8
FORGEJO__security__PASSWORD_COMPLEXITY=lower,upper,digit
FORGEJO__security__PASSWORD_HASH_ALGO=pbkdf2
# Session security
FORGEJO__security__COOKIE_NAME=i_like_forgejo
FORGEJO__security__COOKIE_SECURE=true
FORGEJO__security__LOGIN_REMEMBER_DAYS=7
# CSRF protection
FORGEJO__security__CSRF_TOKEN_GENERATION_INTERVAL=600

Performance Tuning

Optimize Forgejo for your workload:

Terminal window
# HTTP server
FORGEJO__server__HTTP_PORT=3000
FORGEJO__server__PROTOCOL=http
FORGEJO__server__CERT_FILE=
FORGEJO__server__KEY_FILE=
# Performance
FORGEJO__server__ENABLE_GZIP=true
FORGEJO__server__LANDING_PAGE=explore
FORGEJO__server__LFS_START_SERVER=true
# Caching
FORGEJO__cache__ENABLED=true
FORGEJO__cache__ADAPTER=memory
FORGEJO__cache__INTERVAL=60
FORGEJO__cache__HOST=
# Indexer (for code search)
FORGEJO__indexer__ISSUE_INDEXER_TYPE=bleve
FORGEJO__indexer__ISSUE_INDEXER_PATH=/data/indexers/issues.bleve
FORGEJO__indexer__REPO_INDEXER_ENABLED=true
FORGEJO__indexer__REPO_INDEXER_TYPE=bleve
FORGEJO__indexer__REPO_INDEXER_PATH=/data/indexers/repos.bleve

Sample Code: Using Forgejo API

Forgejo provides a comprehensive REST API compatible with Gitea and GitHub APIs. Here are examples in multiple languages:

Ruby - Create a Repository

require 'net/http'
require 'json'
require 'uri'
# Configure API credentials
forgejo_url = 'https://your-app.klutch.sh'
api_token = 'your_api_token_here'
# Create a new repository
uri = URI("#{forgejo_url}/api/v1/user/repos")
request = Net::HTTP::Post.new(uri)
request['Authorization'] = "token #{api_token}"
request['Content-Type'] = 'application/json'
request.body = {
name: 'my-new-repo',
description: 'Created via API',
private: false,
auto_init: true,
default_branch: 'main',
gitignores: 'Go',
license: 'MIT',
readme: 'Default'
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
if response.code == '201'
repo = JSON.parse(response.body)
puts "Repository created: #{repo['html_url']}"
puts "Clone URL: #{repo['clone_url']}"
else
puts "Error: #{response.code} - #{response.body}"
end

Python - List Repositories

import requests
import json
# Configure API credentials
forgejo_url = 'https://your-app.klutch.sh'
api_token = 'your_api_token_here'
headers = {
'Authorization': f'token {api_token}',
'Content-Type': 'application/json'
}
# List all user repositories
response = requests.get(
f'{forgejo_url}/api/v1/user/repos',
headers=headers,
params={'limit': 50, 'page': 1}
)
if response.status_code == 200:
repos = response.json()
print(f"Found {len(repos)} repositories:")
for repo in repos:
print(f"\n{repo['full_name']}")
print(f" Private: {repo['private']}")
print(f" Clone: {repo['clone_url']}")
print(f" Stars: {repo['stars_count']}")
print(f" Forks: {repo['forks_count']}")
else:
print(f"Error: {response.status_code} - {response.text}")

JavaScript - Create an Issue

const axios = require('axios');
// Configure API credentials
const forgejoUrl = 'https://your-app.klutch.sh';
const apiToken = 'your_api_token_here';
// Create a new issue
async function createIssue(owner, repo, issueData) {
try {
const response = await axios.post(
`${forgejoUrl}/api/v1/repos/${owner}/${repo}/issues`,
{
title: issueData.title,
body: issueData.body,
labels: issueData.labels || [],
assignees: issueData.assignees || [],
milestone: issueData.milestone || null
},
{
headers: {
'Authorization': `token ${apiToken}`,
'Content-Type': 'application/json'
}
}
);
const issue = response.data;
console.log(`Issue created: #${issue.number}`);
console.log(`Title: ${issue.title}`);
console.log(`URL: ${issue.html_url}`);
return issue;
} catch (error) {
console.error('Error creating issue:', error.response?.data || error.message);
throw error;
}
}
// Usage
createIssue('username', 'my-repo', {
title: 'Bug: Application crashes on startup',
body: 'Detailed description of the bug...',
labels: ['bug', 'high-priority'],
assignees: ['developer1']
})
.then(issue => console.log('Success:', issue))
.catch(err => console.error('Failed:', err));

Go - Clone and Commit

package main
import (
"fmt"
"log"
"os"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)
func main() {
// Clone repository
cloneURL := "https://your-app.klutch.sh/username/my-repo.git"
username := "your-username"
token := "your_api_token"
dir := "/tmp/my-repo"
fmt.Println("Cloning repository...")
repo, err := git.PlainClone(dir, false, &git.CloneOptions{
URL: cloneURL,
Auth: &http.BasicAuth{
Username: username,
Password: token,
},
Progress: os.Stdout,
})
if err != nil {
log.Fatal(err)
}
// Create a new file
filename := dir + "/README.md"
content := []byte("# Updated via Go\n\nThis file was updated programmatically.")
if err := os.WriteFile(filename, content, 0644); err != nil {
log.Fatal(err)
}
// Stage the file
w, err := repo.Worktree()
if err != nil {
log.Fatal(err)
}
if _, err := w.Add("README.md"); err != nil {
log.Fatal(err)
}
// Commit
commit, err := w.Commit("Update README via API", &git.CommitOptions{
Author: &object.Signature{
Name: "API User",
Email: "api@example.com",
When: time.Now(),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Commit created: %s\n", commit.String())
// Push
fmt.Println("Pushing changes...")
if err := repo.Push(&git.PushOptions{
Auth: &http.BasicAuth{
Username: username,
Password: token,
},
}); err != nil {
log.Fatal(err)
}
fmt.Println("Changes pushed successfully!")
}

PHP - Manage Webhooks

<?php
// Configure API credentials
$forgejoUrl = 'https://your-app.klutch.sh';
$apiToken = 'your_api_token_here';
$owner = 'username';
$repo = 'my-repo';
// Function to create a webhook
function createWebhook($forgejoUrl, $apiToken, $owner, $repo, $webhookUrl) {
$ch = curl_init();
$data = [
'type' => 'forgejo',
'config' => [
'url' => $webhookUrl,
'content_type' => 'json',
'secret' => bin2hex(random_bytes(16))
],
'events' => ['push', 'pull_request', 'issues'],
'active' => true
];
curl_setopt_array($ch, [
CURLOPT_URL => "$forgejoUrl/api/v1/repos/$owner/$repo/hooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
"Authorization: token $apiToken",
"Content-Type: application/json"
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
$webhook = json_decode($response, true);
echo "Webhook created successfully!\n";
echo "ID: {$webhook['id']}\n";
echo "URL: {$webhook['config']['url']}\n";
echo "Secret: {$webhook['config']['secret']}\n";
return $webhook;
} else {
echo "Error: $httpCode - $response\n";
return null;
}
}
// Function to list webhooks
function listWebhooks($forgejoUrl, $apiToken, $owner, $repo) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "$forgejoUrl/api/v1/repos/$owner/$repo/hooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: token $apiToken",
"Content-Type: application/json"
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$webhooks = json_decode($response, true);
echo "Found " . count($webhooks) . " webhooks:\n\n";
foreach ($webhooks as $webhook) {
echo "ID: {$webhook['id']}\n";
echo "URL: {$webhook['config']['url']}\n";
echo "Events: " . implode(', ', $webhook['events']) . "\n";
echo "Active: " . ($webhook['active'] ? 'Yes' : 'No') . "\n\n";
}
return $webhooks;
} else {
echo "Error: $httpCode - $response\n";
return null;
}
}
// Usage
echo "Creating webhook...\n";
createWebhook($forgejoUrl, $apiToken, $owner, $repo, 'https://hooks.example.com/forgejo');
echo "\n\nListing all webhooks...\n";
listWebhooks($forgejoUrl, $apiToken, $owner, $repo);
?>

Production Best Practices

Security Hardening

  1. Use Strong Database Passwords: Generate secure passwords for database credentials.
Terminal window
# Generate secure password
openssl rand -base64 32
  1. Enable HTTPS Only: Klutch.sh provides automatic HTTPS, but ensure your ROOT_URL uses https://.
  2. Restrict Registration: For private instances, disable public registration:
Terminal window
FORGEJO__service__DISABLE_REGISTRATION=true
FORGEJO__service__REQUIRE_SIGNIN_VIEW=true
  1. Configure Rate Limiting: Protect against abuse with rate limits:
Terminal window
FORGEJO__server__ENABLE_RATE_LIMIT=true
FORGEJO__server__RATE_LIMIT_BURST=10
FORGEJO__server__RATE_LIMIT_INTERVAL=60s
  1. Enable Two-Factor Authentication: Encourage or require 2FA for users.
  2. Regular Security Updates: Keep Forgejo updated to the latest version:
FROM codeberg.org/forgejo/forgejo:11
# Check releases: https://codeberg.org/forgejo/forgejo/releases
  1. Audit Logs: Monitor admin actions and security events through Forgejo's admin panel.

Performance Optimization

  1. Use PostgreSQL: For production deployments with multiple users, use PostgreSQL instead of SQLite.
  2. Enable Caching: Configure Redis for improved performance (optional):
Terminal window
FORGEJO__cache__ENABLED=true
FORGEJO__cache__ADAPTER=redis
FORGEJO__cache__HOST=redis-app.klutch.sh:8000
  1. Enable Indexing: Improve search performance with code indexing:
Terminal window
FORGEJO__indexer__REPO_INDEXER_ENABLED=true
FORGEJO__indexer__REPO_INDEXER_TYPE=bleve
  1. Configure Git LFS: For large files, enable Git LFS:
Terminal window
FORGEJO__lfs__START_SERVER=true
FORGEJO__lfs__STORAGE_TYPE=local
FORGEJO__lfs__PATH=/data/lfs
  1. Optimize Database: Regularly maintain your database with VACUUM (PostgreSQL) or OPTIMIZE (MySQL).

Backup Strategy

  1. Volume Backups: Regularly backup the /data volume containing repositories and configuration.
Terminal window
# Backup script example
tar -czf forgejo-backup-$(date +%Y%m%d).tar.gz /path/to/data/volume
  1. Database Backups: If using PostgreSQL, backup the database separately:
Terminal window
# PostgreSQL backup
pg_dump forgejo > forgejo-db-backup-$(date +%Y%m%d).sql
# Compressed backup
pg_dump forgejo | gzip > forgejo-db-backup-$(date +%Y%m%d).sql.gz
  1. Automated Backups: Schedule automated backups daily or weekly depending on activity level.
  2. Test Restores: Periodically test backup restoration to ensure data integrity.
  3. Offsite Storage: Store backups in a separate location or cloud storage (S3, Backblaze B2).

Monitoring and Logging

  1. Application Logs: Monitor Forgejo logs for errors and warnings:
Terminal window
FORGEJO__log__MODE=console,file
FORGEJO__log__LEVEL=Info
FORGEJO__log__ROOT_PATH=/data/forgejo/log
  1. Health Checks: Use Forgejo's built-in health endpoint for monitoring:
Terminal window
curl https://your-app.klutch.sh/api/healthz
  1. Webhook Monitoring: Monitor webhook deliveries in the admin panel.
  2. Resource Monitoring: Track CPU, memory, and disk usage through Klutch.sh dashboard.
  3. Database Monitoring: Monitor database performance, especially query times and connection pool usage.

Scaling Recommendations

  1. Vertical Scaling: Start with 1-2 GB RAM, scale to 4-8 GB for larger teams (100+ users).
  2. Storage Scaling: Monitor repository growth and expand volume size as needed.
  3. Database Optimization: Add indexes for frequently queried fields, tune connection pools.
  4. Consider Caching: Deploy Redis for caching frequently accessed data.
  5. Load Balancing: For high availability, run multiple Forgejo instances behind a load balancer (requires shared storage and database).

Troubleshooting

Cannot Access Forgejo Web Interface

Problem: Unable to access Forgejo at the deployed URL.

Solution:

  • Verify deployment status in Klutch.sh dashboard
  • Check that HTTP traffic is enabled and port 3000 is configured
  • Review deployment logs for startup errors
  • Ensure FORGEJO__server__ROOT_URL matches your actual URL
  • Check that persistent volume is properly mounted at /data

Database Connection Errors

Problem: Forgejo fails to connect to PostgreSQL or MySQL database.

Solution:

  • Verify database is deployed and accessible
  • Check database credentials in environment variables
  • Test database connection: telnet postgres-app.klutch.sh 8000
  • Ensure database name exists and user has proper permissions
  • Review Forgejo logs for specific database errors

Git Operations Fail

Problem: Cannot clone, push, or pull repositories.

Solution:

  • For HTTPS: Verify your username and password/token
  • For SSH: Check SSH key is added to your Forgejo account
  • Ensure repository permissions are correctly configured
  • Check Git LFS is enabled if using large files
  • Verify firewall rules allow Git protocol traffic

SSH Port Issues

Problem: Cannot connect to Forgejo via SSH.

Solution:

  • Verify SSH is enabled: FORGEJO__server__DISABLE_SSH=false
  • Check SSH server is running: FORGEJO__server__START_SSH_SERVER=true
  • Use correct SSH URL format: ssh://git@your-app.klutch.sh:2222/user/repo.git
  • Test SSH connection: ssh -p 2222 git@your-app.klutch.sh
  • If custom SSH ports aren’t supported, use HTTPS instead

Installation Wizard Reappears

Problem: Installation wizard shows up after initial setup.

Solution:

  • Check that installation completed successfully
  • Verify INSTALL_LOCK=true in /data/forgejo/conf/app.ini
  • Set environment variable: FORGEJO__security__INSTALL_LOCK=true
  • Ensure persistent volume is properly mounted

Performance Issues

Problem: Slow page loads, timeouts, or unresponsive interface.

Solution:

  • Check resource usage (CPU, memory) in Klutch.sh dashboard
  • Switch from SQLite to PostgreSQL for better performance
  • Enable caching with Redis
  • Optimize database indexes and queries
  • Increase container resources (RAM, CPU)
  • Enable repository indexing for faster code search
  • Clear temporary files and caches in /data/tmp

Email Not Sending

Problem: Email notifications not working.

Solution:

  • Verify SMTP settings are correct
  • Test SMTP connection manually
  • Check SMTP credentials and authentication
  • Enable debug logging: FORGEJO__log__LEVEL=Debug
  • Verify email provider allows SMTP access
  • Check spam folders for test emails

Additional Resources


You now have a fully functional Forgejo instance running on Klutch.sh! Your team can host Git repositories, collaborate on code, manage issues, run CI/CD workflows, and build software together with complete control and privacy. Remember to regularly backup your data, keep Forgejo updated, and monitor performance as your development team grows.