Important setup steps

Email setup

Forgejo can be set to send emails such as a registration confirmation, either with Sendmail (or compatible MTAs like Postfix and msmtp) or by connecting to an SMTP server.

Using SMTP

Directly use SMTP server as relay. This option is useful if you don't want to set up MTA on your instance but you have an account at an email provider.

[mailer]
ENABLED        = true
FROM           = forgejo@example.com
PROTOCOL       = smtps
SMTP_ADDR      = mail.example.com
SMTP_PORT      = 465
USER           = forgejo@example.com
PASSWD         = `mysecurepassword`

Restart Forgejo for the configuration changes to take effect.

To send a test email to validate the settings, go to Forgejo > Site Administration > Configuration > SMTP Mailer Configuration.

To enable users to receive email notifications for pull requests or issues, you can set ENABLE_NOTIFY_MAIL=true in the configuration.

For the full list of options check the Config Cheat Sheet.

[!note] Note Authentication is only supported when the SMTP server communication is encrypted with TLS or HOST=localhost. This is due to protections imposed by the Go internal libraries against STRIPTLS attacks. TLS encryption can be through:

Using Sendmail

Use sendmail command as mailer.

[!note] Note For Internet-facing sites consult the documentation of your MTA for instructions to send emails over TLS. Also set up SPF, DMARC, and DKIM DNS records to make emails sent be accepted as legitimate by various email providers.

[mailer]
ENABLED       = true
FROM          = forgejo@example.com
PROTOCOL      = sendmail
SENDMAIL_PATH = /usr/sbin/sendmail
SENDMAIL_ARGS = "--" ; most "sendmail" programs take options, "--" will prevent an email address being interpreted as an option.

Reverse proxy

Forgejo can live standalone, or behind a reverse proxy.
You may want this for scenarios like:

Forgejo does not need the help of a proxy to do HTTPS, it can do it directly.
Set in SERVER section of the configuration PROTOCOL=https and either set CERT_FILE and KEY_FILE or let Forgejo manage the certificates with ENABLE_ACME=true

Security of subpath

The security model of browsers assumes that interactions on the same-origin are safe.[^1] By using a subpath, this assumption of the browser no longer holds, which can can make using Forgejo insecure if that origin hosts content entered by a user under different sub-paths. Forgejo offers no protection to mitigate this scenario. For example, malicious content on the same origin could host CSRF attacks against Forgejo.

[^1]: A good read about this security model is https://educatedguesswork.org/posts/web-security-model-origin.

NGINX

Basic HTTP

To set up a basic HTTP reverse proxy in nginx, create a file forgejo.conf in /etc/nginx/conf.d and add the following configuration:

server {
    listen 80; # Listen on IPv4 port 80
    listen [::]:80; # Listen on IPv6 port 80

    server_name git.example.com; # Change this to the server domain name.
    merge_slashes off; # Required to handle URL-encoded slashes

    location / {
        proxy_pass http://127.0.0.1:3000; # Port 3000 is the default Forgejo port

        proxy_set_header Connection $http_connection;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        client_max_body_size 512M;
    }
}

Make sure to reload/restart nginx after changing the configuration.

HTTP with a subpath

If you want to serve Forgejo on a subpath, e.g. on http://example.com/code, use the following configuration:

server {
    listen 80; # Listen on IPv4 port 80
    listen [::]:80; # Listen on IPv6 port 80

    server_name example.com; # Change this to the server domain name.
    merge_slashes off; # Required to handle URL-encoded slashes

    location /code/ { # Replace /code here with your subpath
        proxy_pass http://127.0.0.1:3000/;

        proxy_set_header Connection $http_connection;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        client_max_body_size 512M;
    }
}

Make sure to set the Forgejo ROOT_URL configuration key to the URL with the subpath, otherwise links generated by Forgejo will be broken.

HTTPS

When using a reverse proxy, it's usually easier to let the proxy handle HTTPS. It's easy to set up HTTPS on nginx.

HTTPS with Certbot

To set up HTTPS with Certbot, first set up an HTTP reverse proxy with the configuration above and ensure that it works as expected. To use HTTPS you need to have a domain name.

Then, install certbot. When running certbot, select the domain name that your Forgejo instance is hosted under, and choose automatic installation. This should automatically set up HTTPS on port 443 and a redirect on the old port 80.

You may wish to change the ROOT_URL configuration key to the HTTPS protocol so links generated by Forgejo automatically use HTTPS.

HTTPS with manually installed certificates

If you have obtained certificates from elsewhere or have chosen not to let Certbot automatically install them, make the following changes to the configuration file:

Change the listening ports

Change the lines

listen 80;
listen [::]:80;

to

listen 443 ssl;
listen [::]:443 ssl;
http2 on;

Add the SSL certificate information

Generate an SSL configuration at mozilla, and add the SSL parameters to your configuration file. Make sure to replace the paths in the example with paths to your certificate files.

Add a redirect from HTTP

Outside the server block, add this redirection block:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location / {
        return 301 https://$host$request_uri;
    }
}

This will redirect anyone visiting the HTTP site to the HTTPS site.

Apache

Basic HTTP

To set up a basic HTTP proxy in Apache, create a file 100-forgejo.conf in /etc/apache2/sites-available and add the following configuration:

<VirtualHost *:80>
    ServerName git.example.com

    ProxyPreserveHost On
    ProxyRequests off
    AllowEncodedSlashes NoDecode
    ProxyPass / http://127.0.0.1:3000/ nocanon
</VirtualHost>

Next, enable the site with a2ensite 100-forgejo.conf and enable the proxy modules with a2enmod proxy proxy_http. Finally, restart the Apache server.

HTTP with a subpath

If you want to serve Forgejo on a subpath, e.g. on http://example.com/code, use the following configuration:

<VirtualHost *:80>
    ServerName example.com

    ProxyPreserveHost On
    ProxyRequests off
    AllowEncodedSlashes NoDecode
    ProxyPass /code http://127.0.0.1:3000/ nocanon # Change /code here to your desired subpath.
</VirtualHost>

Make sure to set the Forgejo ROOT_URL configuration key to the URL with the subpath, otherwise links generated by Forgejo will be broken.

HTTPS

When using a reverse proxy, it's usually easier to let the proxy handle HTTPS. It's easy to set up HTTPS on Apache.

HTTPS with Certbot

To set up HTTPS with Certbot, first set up an HTTP reverse proxy with the configuration above and ensure that it works as expected. To use HTTPS you need to have a domain name.

Then, install certbot. When running certbot, select the domain name that your Forgejo instance is hosted under, and choose automatic installation. This should automatically set up HTTPS on port 443 and a redirect on the old port 80.

You may wish to change the ROOT_URL configuration key to the HTTPS protocol so links generated by Forgejo automatically use HTTPS.

HTTPS with manually installed certificates

If you have obtained certificates from elsewhere or have chosen not to let certbot automatically install them, make the following changes to the configuration file:

Change the listening ports

Change <VirtualHost *:80> to <VirtualHost *:443>.

Add the SSL certificate information

Generate an SSL configuration at mozilla, and add the SSL parameters to your configuration file. Make sure to replace the paths in the example with paths to your certificate files.

Add a redirect from HTTP

Outside the VirtualHost *:443, add this configuration:

<VirtualHost *:80>
    ServerName git.example.com

    RewriteEngine on
    RewriteCond %{SERVER_NAME} =git.example.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

This will redirect anyone visiting the HTTP site to the HTTPS site.

HAProxy

Basic HTTP

To setup HAProxy on port 80, without a virtualhost, you can add the following stanza to your haproxy.cfg:

listen forgejo_80
  bind :::80 v4v6
  mode http
  timeout connect 10s
  timeout client 30s
  timeout server 30s
  server forgejo 127.0.0.1:3000

HTTPS

To setup basic HTTPS proxying with HAProxy, you can add these blocks to your haproxy configuration:

Redirection to SSL

listen forgejo_80
  bind :::80 v4v6
  mode http
  timeout connect 10s
  timeout client 30s
  timeout server 30s
  redirect scheme https code 301

SSL frontend

frontend forgejo_443
  bind :::443 v4v6 ssl crt /etc/haproxy_certs/forgejo.example.org.pem
  mode http
  option httplog
  option forwardfor
  timeout client 1m
  use_backend forgejo_443 if { ssl_fc_sni forgejo.example.org }

SSL backend

backend forgejo_443
  mode http
  timeout connect 10s
  timeout server 30s
  server forgejo 127.0.0.1:3000

HTTPS with UNIX Socket

A Unix socket has lower latency compared to TCP. When combined with HAProxy, it provides a highly responsive and excellent user interface experience.

We assume that:

[server]
PROTOCOL = http+unix
HTTP_ADDR = /var/lib/haproxy/forgejo/forgejo.sock
UNIX_SOCKET_PERMISSION = 660
...

Now you need to create a directory which can be acceessed either by the chroot environment used by HAProxy, and by Forgejo.

install -o git -g haproxy -m 0770 -d /var/lib/haproxy/forgejo

Finally, you can add these blocks into your haproxy.cfg.

chroot

you include these lines in the global section of your haproxy configuration

global
  chroot  /var/lib/haproxy
  user    haproxy
  group   haproxy
  ...

SSL Backend

The backend configuration will be as follows:

backend forgejo_443
  mode http
  timeout connect 10s
  timeout server 30s
  server forgejo /forgejo/forgejo.sock tfo

note: The Unix socket path is relative to the path of the chroot environment

HAProxy with UNIX socket using Puppet

This configuration relies on Puppetlabs HAProxy module. This code sample is a compromise for the sake of the conciseness.
The Forgejo backend will be available only at the second execution of Puppet, unless you add a statement to create a user in advance, and you don't need to set the dependency for the 2 directories against the HAProxy class. You also need to push the SSL certificate, but all this goes far beyond the scope of this documentation.

  file {
    default:
      notify  => Service['haproxy'],
      require => [Class['haproxy'], User['git']];
    '/etc/haproxy_certs':
      ensure  => directory,
      purge   => true,
      mode    => '0700',
      owner   => haproxy,
      group   => haproxy,
      recurse => true;
    '/var/lib/haproxy/forgejo':
      ensure => directory,
      mode   => '0770',
      owner  => git,
      group  => haproxy;
  }

  class { 'haproxy':
    package_ensure   => $haproxy_version,
    global_options   => {
      'log'                       => "/dev/log local0\n  log  /dev/log local1 notice",
      'chroot'                    => '/var/lib/haproxy',
      'maxconn'                   => '150000',
      'user'                      => 'haproxy',
      'group'                     => 'haproxy',
      'stats'                     => 'socket /var/run/haproxy.sock user root group sensu mode 660 level admin',
      'tune.ssl.default-dh-param' => '2048',
    },
    defaults_options => {
      'default-server' => 'init-addr libc,none',
      'log'            => 'global',
      'retries'        => '5',
      'option'         => ['redispatch', 'http-server-close', 'logasap'],
      'timeout'        => ['http-request 7s', 'connect 5s', 'check 9s'],
      'maxconn'        => '5000',
    };
  }

  haproxy::listen { 'forgejo_80':
    bind    => { ':::80' => ['v4v6'] },
    mode    => 'http',
    options => [
      { 'timeout' => 'connect 10s' },
      { 'timeout' => 'client 1m' },
      { 'timeout' => 'server 1m' },
      { 'redirect' => 'scheme https code 301' },
    ];
  }

  haproxy::frontend { 'forgejo_socket':
    bind    => { ':::443' => ['v4v6', 'ssl', 'crt', '/etc/haproxy_certs/forgejo.example.com.pem'] },
    options => [
      {
        mode   => 'http',
        option => ['httplog', 'forwardfor'],
      },
      { 'timeout' => 'client 1m' },
      { 'use_backend' => "forgejo if { ssl_fc_sni forgejo.example.com }" },
    ];
  }

  haproxy::backend { 'forgejo_socket':
    options => [
      { 'mode' => 'http' },
      { 'timeout' => 'connect 10s' },
      { 'timeout' => 'server 1m' },
      { 'server' => 'forgejo /forgejo/forgejo.sock tfo' },
    ];
  }

Caddy

HTTPS

To set up basic HTTPS proxy in Caddy with Caddyfile create a file forgejo in /etc/caddy/conf.d and add the following configuration:

git.example.com {
  reverse_proxy 127.0.0.1:3000
}

Caddy will automatically get certificates for the domain.

HTTPS with a subpath

If you want to serve Forgejo on a subpath, e.g. on https://example.com/code, use the following configuration:

example.com {
  route /code/* {
    uri strip_prefix /code
    reverse_proxy 127.0.0.1:3000
  }
}

Make sure to set the Forgejo ROOT_URL configuration key to the URL with the subpath, otherwise links generated by Forgejo will be broken.

Proxy Authentication

Forgejo supports Reverse Proxy Header authentication, it will read headers as a trusted login user name or user email address. This hasn't been enabled by default, you can enable it with

[service]
ENABLE_REVERSE_PROXY_AUTHENTICATION = true

The default login user name is in the X-WEBAUTH-USER header, you can change it via changing [security].REVERSE_PROXY_AUTHENTICATION_USER in app.ini. If the user doesn't exist, you can enable automatic registration with ENABLE_REVERSE_PROXY_AUTO_REGISTRATION=true.

The default login user email is X-WEBAUTH-EMAIL, you can change it via changing [security].REVERSE_PROXY_AUTHENTICATION_EMAIL in app.ini, this could also be disabled with ENABLE_REVERSE_PROXY_EMAIL

If set ENABLE_REVERSE_PROXY_FULL_NAME=true, a user full name expected in X-WEBAUTH-FULLNAME will be assigned to the user when auto creating the user. You can also change the header name with [security].REVERSE_PROXY_AUTHENTICATION_FULL_NAME.

You can also limit the reverse proxy's IP address range with [security].REVERSE_PROXY_TRUSTED_PROXIES which default value is 127.0.0.0/8,::1/128. By [security].REVERSE_PROXY_LIMIT, you can limit trusted proxies level.

Notice: Reverse Proxy Auth doesn't support the API. You still need an access token or basic auth to make API requests.

Docker / Container Registry

The container registry uses a fixed sub-path /v2 which can't be changed. Even if you deploy Forgejo with a different sub-path, /v2 will be used by the docker client. Therefore you may need to add an additional route to your reverse proxy configuration.

Storage settings

The storage for each subsystem is defined in app.ini. It can either be on disk (local which is the default) or using a S3 compatible server (minio). In both cases each subsystem stores all files (or objects in the S3 parlance) in a dedicated directory as shown in the table below:

subsystem directory app.ini sections
Attachments attachments/ [attachment]
LFS lfs/ [lfs]
Avatars avatars/ [avatar]
Repository avatars repo-avatars/ [repo-avatar]
Repository archives repo-archive/ [repo-archive]
Packages packages/ [packages]
Actions logs actions_log/ [storage.actions_log]
Actions Artifacts actions_artifacts/ [actions.artifacts]

For instance:

Changing the storage for all subsystems

The [storage] section can be used to modify the storage of all subsystems. The default is to use local storage under APP_DATA_PATH and is equivalent to writing the following in app.ini:

[server]
APP_DATA_PATH = /forgejo/data

[storage]
STORAGE_TYPE = local
PATH = /forgejo/data

Using local

For local storage, the [storage] section can only be used to change the path under which all subsystems directories will be created, using the PATH settings with an absolute pathname.

For instance:

[storage]
STORAGE_TYPE = local
PATH = /mystorage

will change the default for storing attachments to /mystorage/attachments, LFS to /mystorage/lfs etc.

Using minio

The [storage] section can be used to change the default storage type used by all subsystems to minio.

For instance:

[storage]
STORAGE_TYPE = minio

MINIO_ENDPOINT = 127.0.0.1:9000
MINIO_ACCESS_KEY_ID = [redacted]
MINIO_SECRET_ACCESS_KEY = [redacted]
MINIO_BUCKET = forgejo
MINIO_LOCATION = us-east-1

will change the default for storing attachments to attachments/ in the forgejo bucket, LFS to lfs/ in the forgejo bucket etc.

[!note] Note MINIO_BASE_PATH must not be set in the [storage] section.

The configuration option MINIO_USE_SSL defaults to false to maintain compatibility with locally hosted MinIO instances. If an external S3 provider is intended to be used, this option should be set to true.

For instance, assuming a MinIO instance at https://minio.example.com:

[storage]
STORAGE_TYPE = minio

MINIO_USE_SSL = true
MINIO_ENDPOINT = minio.example.com
MINIO_ACCESS_KEY_ID = [redacted]
MINIO_SECRET_ACCESS_KEY = [redacted]
MINIO_BUCKET = bucket
MINIO_LOCATION = us-east-1

Storage settings for a single subsystem

It is possible to configure some subsystems to use S3 storage and others to use local storage by adding settings to their respective sections. For instance:

[attachment]
PATH = /otherstorage/attachments

[lfs]
STORAGE_TYPE = minio
MINIO_BASE_PATH = lfs/

MINIO_ENDPOINT = 127.0.0.1:9000
MINIO_ACCESS_KEY_ID = [redacted]
MINIO_SECRET_ACCESS_KEY = [redacted]
MINIO_BUCKET = forgejo
MINIO_LOCATION = us-east-1

will store attachments in the local directory /otherstorage/attachments while lfs files will be stored in the S3 server within the lfs/ directory of the forgejo bucket.

Storage settings

The value of STORAGE_TYPE can be local (the default) for file system directories or minio for S3 servers. Each storage type has its own settings, as explained below.

Using local

There is just one setting when the STORAGE_TYPE is set to local: PATH. It must be an absolute path and is interpreted as follows.

In the [storage] section, PATH is the path under which the directories of each subsystem will be created instead of APP_DATA_PATH. For instance, if APP_DATA_PATH equals /appdata

[storage]
STORAGE_TYPE = local
PATH = /mystorage

will create attachments in /mystorage/attachments instead of /appdata/attachments, LFS files in /mystorage/lfs instead of /appdata/lfs, etc.

In the section dedicated to a subsystem (see the table in the introduction), PATH is the base path under which all files will be stored. For instance:

[storage]
STORAGE_TYPE = local
PATH = /mystorage

[attachment]
STORAGE_TYPE = local
PATH = /otherstorage/attachments

will store attachments in /otherstorage/attachments while lfs files will be stored in /mystorage/lfs.

Using minio

When the STORAGE_TYPE is set to minio, the settings are used to to connect to a S3 compatible server:

When used in the [storage] section they apply to all subsystems. When used in the section specific to a subsystem (see the table in the introduction), they are only used for objects that belong to this subsystem. Here is a example:

[storage]
STORAGE_TYPE = minio

SERVE_DIRECT = false
MINIO_ENDPOINT = garage:9000
MINIO_ACCESS_KEY_ID = [redacted]
MINIO_SECRET_ACCESS_KEY = [redacted]
MINIO_BUCKET = forgejo
MINIO_BUCKET_LOOKUP = auto
MINIO_LOCATION = us-east-1
MINIO_USE_SSL = false
MINIO_INSECURE_SKIP_VERIFY = false
MINIO_CHECKSUM_ALGORITHM = md5

[lfs]
STORAGE_TYPE = minio
MINIO_BASE_PATH = nonstandardlfs/

SERVE_DIRECT = false
MINIO_ENDPOINT = minio:9000
MINIO_ACCESS_KEY_ID = [redacted]
MINIO_SECRET_ACCESS_KEY = [redacted]
MINIO_BUCKET = forgejo
MINIO_BUCKET_LOOKUP = auto
MINIO_LOCATION = us-east-1
MINIO_USE_SSL = false
MINIO_INSECURE_SKIP_VERIFY = false

For all subsystems that use the minio storage type found in the [storage] section, the directory in which the objects are stored is determined using the table in the introduction. For instance LFS files will be stored in the lfs/ directory within the forgejo bucket.

When the minio storage is set in a section specific to a subsystem, the MINIO_BASE_PATH setting can be used to override the default directory. In the example above, MINIO_BASE_PATH = nonstandardlfs/ means LFS objects will be stored in the nonstandardlfs/ directory within the forgejo bucket instead of the lfs/ directory

S3 servers compatibility

Although the S3 storage type is named minio it does not rely on any MinIO specific features. The S3 storage type is tested to be compatible with:

Undocumented features

It is strongly recommended to avoid using undocumented features - such as [storage.attachments] as an alternative to [attachment] for instance (the plural is not a typo, it is a unification problem) - because their behavior is not thoroughly tested and may lead to unexpected results.

Legacy settings

Some settings are deprecated but still supported in the interest of backward compatibility. They should be replaced as follows:

Legacy settings have a lower priority and will be overridden by their replacement if both are present. For instance:

[picture]
AVATAR_UPLOAD_PATH = /legacy_path

[avatar]
PATH = /avatar_path

will store avatar files in /avatar_path.

Recommended Settings and Tips

The default settings are applicable to a broad range of Forgejo instances. Nevertheless, not every instance has the same purpose and number of users. This document lists settings suggested for various situations, the results of such configuration changes, and their use cases.

In this document, configuration parameters are described as [section.part].ENABLE_FEATURE_XY, with section and .part indicating the section and subsection accordingly, and ENABLE_FEATURE_XY reflecting the key. As an example, here's how it would look like in the app.ini file:

[section.part]
ENABLE_FEATURE_XY = new_value

Please bear in mind that you don't have to create new sections for every value; you can have multiple settings in the same section, such as:

[section.part]
ENABLE_FEATURE_XY = new_value
DISABLE_FEATURE_Z = other_new_value

[database].DB_TYPE

For every action, operation or page you visit, Forgejo must query a database for data. The database is an important part of the Forgejo stack and therefore must be configured with care to ensure proper operation. However, it is difficult to give a recommendation due to its general nature and the many factors that affect the use of the database. So keep the following text in mind when configuring and choosing the database and always test carefully if the configuration suits your situation and ask for assistance if needed.

If your instance sees a low to moderate amount of activity, it is recommended to change this value to sqlite3. SQLite3 is a simple, non-maintenance requirement and one file on disk database. It is by far the easiest database to configure and has many other advantages over the other databases, but it becomes a poor choice once you see a lot of concurrent activity in which case performance may decrease, but SQLite can go a long way in the early years of an instance.

If your instances see a high amount of activity, it is recommended to change this value to mysql or postgres. There is no best managed database server, and it mainly depends on your previous experience and knowledge about the database you want to use, both can handle large amounts of activity, please refer to the database-configuration about what other values should be configured to connect to the database server.

[!warning] Warning Keep in mind that transitioning to another database with an existing database is not a trivial task and must be done carefully.

[cache].ADAPTER

Forgejo uses caching to avoid repeating expensive operations. The default cache implementation is basic: items expire after a fixed duration (time-to-live) regardless of usage frequency, and it lacks eviction strategies based on memory pressure or access patterns (like LRU). This simplicity means it may not be optimal for all workloads. To illustrate the potential impact of caching, consider these common use cases:

If your instance does not see much activity, it is recommended to change this value to twoqueue. This will use a size-limited LRU cache, which will keep frequently used items and remove the ones that are not used often. It is also recommended to change [cache].HOST to {"size":100, "recent_ratio":0.25, "ghost_ratio":0.5} because the default value has a limit of 50,000 items and since by default it will keep items for 16 hours, it is not memory efficient to possibly keep so many items for a small instance.

If your instance sees a lot of activity, it is recommended to change this value to redis or memcache. In that case, caching is outsourced to third-party software designed to cache items. Keep in mind that you will probably also need to modify [cache].HOST to configure the use of this software.

The redis adapter should support most software that works with a limited subset of the Redis Go Client APIs. In particular, Forgejo has integration tests against:

[repository.signing].DEFAULT_TRUST_MODEL

When Forgejo needs to verify a GPG or SSH signed commit on a repository, it checks who it can trust to have a verified commit on that repository. The default value for this setting is that Forgejo trusts only contributors to that repository to have signed commits, this can cause unexpected behavior for those used to GitHub when hosting repositories that anyone can contribute to, because non-collaborators with signed commits on that repository are shown as unverified.

If your instance expects users to contribute to other repositories within your instance, it is recommended that you change this value to committer. In that case, signed commits will appear as verified even if they are not collaborators.

[security].LOGIN_REMEMBER_DAYS

When a user logs in with the remember option enabled, they receive a long-term authentication cookie that is remembered for a number of days, determined by this setting. The default value is one month, which may be less than other services and could be frustrating for the user to log in every month.

If your instance does not need to adhere to a security policy that mandates a different value, it is recommended that you change this value to 365. In that case, the long-term authentication cookie will be stored on the user's device for one year.

[service].ENABLE_CAPTCHA

In recent years, spam bots have gotten smarter and have automated more than ever. A simple register form such as in Forgejo is a simple task for bots to register a new user and comment wherever they can about their favorite scam website. Forgejo provides the option to enable captcha on the register form.

If your instance has an open registration, it is recommended to change this value to true. You also need to choose which captcha you want to use, you can find more about that in the service configuration. Keep in mind that certain types of captcha limit the accessibility of registering new users because they often use images that are difficult to see.

[ui].ONLY_SHOW_RELEVANT_REPOS

The explore page is a good way to quickly check what is happening with an instance because it shows the most recent repositories on which action has been taken. If your instance sees a lot of activity, such as with a large user base, this explore page can quickly become a cluttered place where most of the repositories may not be relevant, such as a test repository or someone's personal dotconfig repository, which does not necessarily add to the effect of seeing what is happening on that instance.

If you have an instance with a lot of activity, it is recommended to change this value to true. In that situation, the explore page will filter out repositories that are likely to be less relevant in order to see what interesting projects are available on that instance.

[service].USERNAME_COOLDOWN_PERIOD

If a user or an organization is renamed, each old username, until it is claimed, will redirect to the latest username. In the context of public forges, to prevent namesquatting and allow users to reverse their username change within a certain period of time, a cooldown period can be configured to ensure that no user can claim the username within the cooldown period (with the exception of the original user and administrators).

If your instance has an open registration, it is recommended to change this value to 7. This means that there is a seven-day cooldown period for old usernames before they can be claimed by anyone, and that users can revert to their old username within seven days.

There is a closely related configuration option [service].MAX_USER_REDIRECTS for which the default value is 5 when a cooldown period is specified. This controls the number of user redirects a user can have (and therefore how many usernames are subject to a cooldown period). The intention of this option is to prevent abuse of the cooldown feature by limiting the amount of usernames that one user can keep on cooldown.

OpenSSH recommendations

This section only applies if you've enabled SSH (enabled by default), and are using sshd provided by OpenSSH on the host. Using OpenSSH is Forgejo's recommended practice. Forgejo also provides a built-in SSH server ([ssh].START_SSH_SERVER set to true) where these OpenSSH recommendations would not be applicable.

We recommend that you scope these settings in the sshd_config for the git user (or another user if it's set differently in [ssh].SSH_USER). All the recommendations below match on the git user.

Git's wire protocol v2

Git will tell the server over SSH that it is able to use a newer version of the Git Wire Protocol, but by default, SSH servers like OpenSSH will not pass the environment to Forgejo.

You have to tell the SSH server to pass the GIT_PROTOCOL environment, which can be configured as follows:

Match User git
  AcceptEnv GIT_PROTOCOL

Per source penalty

This only applies if you reverse proxy SSH connections, and thus sshd do not get the original client IP.

Since OpenSSH 9.8, sshd will penalize sources (IP addresses) if they do something other than authenticating correctly. Thus, in the case that sshd only ever sees the IP address of your proxy software, then after enough penalties it will block all incoming SSH connections. This is undesirable as legitimate users are now penalized by the bad behavior of malicious users, as such we recommend to disable the feature altogether as follows:

Match User git
  PerSourcePenalties no

If you still want to have this feature while using a reverse proxy, you have two options. sshd doesn't support the PROXY protocol, so you either you have to use mmproxy[^1] or transparent proxying if your proxy software supports that. Both require changes in the firewall setup and transparent proxy additionally requires kernel parameters changes, which might be undesirable for your use case.

[^1]: Cloudflare introduced the concept of mmproxy, but we recommend go-mmproxy and mmproxy-rs as implementation over the implementation by Cloudflare.

AuthorizedKeys command

This is mostly applicable if you administer a Forgejo instance for many users, as this solves a scaling problem.

The default configuration is to create a authorized_keys file in the git user's .ssh directory, which contains all public SSH keys that users have uploaded to their account. sshd reads the entire file[^2] when verifying if the public key is accepted, this in turn can lead to many read syscalls if the authorized_keys file is of a large size.

[^2]: Yes, it really consumes the whole file. https://github.com/openssh/openssh-portable/blob/f44f124a234785b9d2cd59a6da28737fb3c4ad9a/auth2-pubkeyfile.c#L433-L435

Forgejo can provide the authorized keys on-demand to sshd, by configuring AuthorizedKeysCommand. If you compile forgejo from source, it should be noted that sshd requires the binary to be owned by root and not not writable by groups and others. Furthermore you must specify the full path to the forgejo binary.

Match User git
  AuthorizedKeysCommandUser git
	AuthorizedKeysCommand /usr/bin/forgejo --config /etc/forgejo/conf/app.ini keys -u %u -t %t -k %k

As of writing, you will run into another scaling problem. The whole config is parsed on each execution, depending on your config this might take a few seconds.

Tips

Database performance

When trying to understand poor database performance in conjunction with Forgejo, it is useful to set [database].SLOW_QUERY_THRESHOLD to a value lower than the default (5 seconds). This ensures that SQL queries that are slow, but not too slow to meet the default value, are logged and can provide insight into what kind of SQL queries are slow, moreover, this is useful information for Forgejo contributors to find the code where this SQL query is executed and understand the context.

Authentication

LDAP (Lightweight Directory Access Protocol)

Both the LDAP via BindDN and the simple auth LDAP share the following fields:

LDAP via BindDN

Adds the following fields:

LDAP using simple auth

Adds the following fields:

Verify group membership in LDAP

Uses the following fields:

PAM (Pluggable Authentication Module)

This procedure enables PAM authentication. Users may still be added to the system manually using the user administration. PAM provides a mechanism to automatically add users to the current database by testing them against PAM authentication. To work with normal Linux passwords, the user running Forgejo must also have read access to /etc/shadow in order to check the validity of the account when logging in using a public key.

Note: If a user has added SSH public keys into Forgejo, the use of these keys may bypass the login check system. Therefore, if you wish to disable a user who authenticates with PAM, you should also manually disable the account in Forgejo using the built-in user manager.

  1. Configure and prepare the installation.
    • It is recommended that you create an administrative user.
    • Deselecting automatic sign-up may also be desired.
  2. Once the database has been initialized, log in as the newly created administrative user.
  3. Navigate to the user settings (icon in the top-right corner), and select Site Administration -> Authentication Sources, and select Add Authentication Source.
  4. Fill out the fields as follows:
    • Authentication Type: PAM
    • Name: Any value should be valid here; use "System Authentication" if you'd like.
    • PAM Service Name: Select the appropriate file listed under /etc/pam.d/ that performs the authentication desired.[^1]
    • PAM Email Domain: The e-mail suffix to append to user authentication. For example, if the login system expects a user called gituser, and this field is set to mail.com, then Forgejo will expect the user email field for an authenticated GIT instance to be gituser@mail.com.[^2]

Note: PAM support is added via build-time flags (TAGS="pam" make build), and the official binaries provided do not have this enabled. PAM requires that the necessary libpam dynamic library be available and the necessary PAM development headers be accessible to the compiler.

[^1]: For example, using standard Linux log-in on Debian "Bullseye" use common-session-noninteractive - this value may be valid for other flavors of Debian including Ubuntu and Mint; consult your distribution's documentation.

[^2]: This is a required field for PAM. Be aware: In the above example, the user will log into the Forgejo web interface as gituser and not gituser@mail.com.

FreeIPA