Setting Up a Phorge Instance

May 11, 2025

Introduction to Phorge

phorge is the successor project to phabricator after that project ceased operation in June, 2021. phorge is essentially a team-based code review tool capable of supporting large organizations that use it with source code management tools like git, SVN, or Mercurial. It enables pre-commit review, tracking changes (down to individual characters) and enables easy comparison of earlier and later revisions. Like phabricator, phorge uses differentials, queues, reviews, and kanban boards to manage software development workflows. The project is managed by a community driven effort using phorge itself to guide future development.

Figure 1: Phorge, the Successor to Phabricator

Figure 1: Phorge, the Successor to Phabricator

I ran across this product as a documentation contributor to FreeBSD where it is used to review not only documentation, but source code modifications to the base system and ports. In this use case it is also paired with arcanist a command-line tool for interacting with a phorge instance for formatting and staging reviews, along with some custom scripting.

This techbits article will show you how to set up and run a phorge instance on FreeBSD.

Directory Structure

The FreebSD package for phorge has already laid out the directory structure for using phorge.

The directory layout for phorge is:

    /usr/local/lib/php/phorge
                             /conf
                             /local
                                 local.json.sample
                                 local.json
                             /resources
                             /webroot

You will need:

  • A newly installed FreeBSD instance. This example used 15-CURRENT.
  • A computer (laptop, desktop, virtual machine), preferably an AMD or Intel system with at least 8 GB of memory. This will be the server machine, noted as such throughout this article.
  • A large, fast disk (SSD preferred) with at least 256GB is recommended
  • An Internet connection (gigabit speed recommended)
  • A second machine, the client, that will connect to the server
  • A graphical environment on the client for typical web browsing

You will also need an Internet domain name from a registrar. Here, the domain name ptest.example.org is used.

  ptest.example.org.   3600  IN  A  123.123.123.123

If you want to use email for user setup and maintenance you will also need an MX record:

  ptest.example.org.   3600  IN  MX  10  ptest.example.org.

You will probably also need SPF, DMARC, and DKIM set up. Setting up a mail server is beyond the scope of this article, but there are many good references on the internet.

Step by step instructions follow.

Host Setup

FreeBSD 15-CURRENT has the required packages listed in this article. Other versions may work as well.

To download the FreeBSD installation .iso visit: https://www.freebsd.org/releases/ Help on the installtion of FreeBSD is provided in the FreeBSD Handbook.

OS Installation and Setup

If you already have FreeBSD installed on your host machine, skip to the next section. Install FreeBSD on the host machine. Note that the default user (here ‘jpb’) should be a member of the wheel group. Add a standard user named phorge. User phorge does not need OS administrative privileges.

Add SSH keys:

Set up ssh keys with ssh-keygen for jpb and phorge (the default is ed25519)

  $ ssh-keygen

  Add any remote keys needed to both jpb and phorge .ssh/authorized_keys files.

Add the following packages.

Installation, even over a fast Internet connection, will take some time. I suggest running script(1) before installing these packages. When completed, review the ’typescript’ file for any errors, omissions, or notifications.

Create a file named package_list.txt with these contents:

  ImageMagick7
  bash
  cmdwatch
  cyrus-sasl
  git
  groff
  jq
  lynx
  mutt
  mysql84-server
  nginx
  opendkim
  phorgeit-arcanist-lib-php83
  phorgeit-arcanist-php83
  phorgeitphorge-php83
  php-fpm_exporter
  pkg
  postfix-sasl
  postgrey
  py311-spf-engine
  py311-certbot
  py311-certbot-nginx
  py311-pygments
  rsync
  sudo
  tmux
  zip

Install the packages in the file:

  # script 
  Script started, output file is typescript 
  # 
  # for i in  `cat pkg_list.txt` 
  do 
    echo 
    echo "-----  ${i}  ---------------------------------------------" 
    pkg install -y ${i} 
    echo 
    echo 
  done 

Note: the phorge packages will put all phorge and php code in /usr/local/lib/php/phorge and subdirectories. You do not need to pull any code from the we.phorge.it website.

rc.conf, MySQL, and PHP Setup

Set up /etc/rc.conf, editing the file directly, or using sysrc(1):

  hostname="ptest.example.org"
  nginx_enable="YES"
  mysql_enable="YES"
  #php_fpm_enable="YES"
  #phd_enable="YES"
  #sendmail_enable="NONE"
  # git_daemon_enable="YES"

but comment some out as shown.

Nginx should work immediately.

  # service nginx start

   (Use lynx to test. Should bring up the nginx welcome screen.)

  $ lynx localhost

MySQL

The first use of mysql requires no password:

  # mysql -u root

    (Set the mysql root user password immediately with:)

    ALTER USER 'root'@'localhost' IDENTIFIED BY 'secretpassword';

Exit and test logging in with new password.

Next, run


  # mysql_secure_installation

to set up mysql security.

Disallow root logins at a minimum. The other questions are user choice.

Copy /usr/local/etc/mysql/my.cnf.sample to /usr/local/etc/mysql/my.cnf Edit the file and in section [mysqld] add the line:

  # cp /usr/local/etc/mysql/my.cnf.sample  /usr/local/etc/mysql/my.cnf
  
  [mysqld]
  sql_mode	           = STRICT_ALL_TABLES

Exit restart mysqld.

  # service mysql-server restart

There should be no errors.

For PHP, copy /usr/local/etc/php.ini-production to php.ini

Edit /usr/local/etc/php.ini as follows:

  set error_log = /var/log/php_errors.log
  set post_max_size = 32M
  set upload_max_filesize = 32M
  set date.timezone = America/New_York     (or as appropriate)
  set opcache.validate_timestamps=1        (see note on phorge notification)

The php_errors.log will need to be created before first use:

  # touch /var/log/php_errors.log

Next, edit /usr/local/etc/php-fpm.d/www.conf to use a unix socket for communication:

  ; The address on which to accept FastCGI requests. 
  ; Valid syntaxes are: 
  ;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4 address on 
  ;                            a specific port; 
  ;   '0.0.0.0:port'         - to listen on a TCP socket to all IPv4 addresses on 
  ;                            a specific port; 
  ;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on 
  ;                            a specific port; 
  ;   'port'                 - to listen on a TCP socket to all addresses 
  ;                            (IPv6 and IPv4-mapped) on a specific port; 
  ;                            Note: IPv4-mapped addresses are disabled by-default in 
  ;                                  FreeBSD for security reasons; 
  ;   '/path/to/unix/socket' - to listen on a unix socket. 
  ; Note: This value is mandatory. 
  ; jpb changed listen parameter to switch to unix socket. Had trouble using both ipv4 and ipv6 at the same time. 
  ;listen = 127.0.0.1:9000 
  listen = /var/run/php-fpm.sock 

Phorge local.json modifications

The local.json file can be edited directly or edited by using the phorge specific config command: Either edit the file directly or use $T/bin/config which will work, but will complain about connection errors to the database. The easiest way to do this is to set a variable to access the phorge base directory, and run the config command from there:

  # export T=/usr/local/lib/php/phorge/
  # cd $T/conf/local
  # cp local.json.sample  local.json   # this is the phorge local config file.

Set up "standard credentials":

  # $T/bin/config set mysql.host  localhost
  # $T/bin/config set mysql.user  phorge
  # $T/bin/config set mysql.pass  phorgemysqlpassword

Review local.json for all fields. See file below.

Nginx and Phorge Certificate Setup With Certbot

Start nginx with default config to set up let’s encrypt certbot.

  # service nginx start

Test with lynx localhost - this should bring up the nginx welcome screen.

Setting up Let’s Encrypt certbot

  # certbot run --nginx
or
  # certbot
and follow prompts 

domains:   ptest.example.org 

The certbot may indicate it successfully retreived the certificate but could not install it. The certificate and key should be in:

  /usr/local/etc/letsencrypt/live/ptest.example.org/fullchain.pem
  /usr/local/etc/letsencrypt/live/ptest.example.org/privkey.pem

Make the necessary changes to nginx.conf. See nginx.conf file below.

You must complete this step to receive certificate and key from Let’s Encrypt.

To check and renew weekly, edit /etc/periodic.conf

  weekly_certbot_enable="YES"

You will also need a dhparam file for the certificate. I’ve placed this file in the Let’s Encrypt “live” directory:

  # cd /usr/local/etc/letsencrypt/live/ptest.example.org
  # openssl dhparam -out dhparam.pem -outform PDM 2048

This may take several minutes.

Now, finalize the nginx.conf file with these steps:

Reset configuration for nginx to the file below.

  # service nginx stop

Copy nginx configuration from file below to /usr/local/etc/nginx.

Now, ensure edits to server_name, and root directory are correct:

  server_name  ptest.example.org;
  root         /usr/local/lib/php/phorge/webroot;

  ssl_certificate /usr/local/etc/letsencrypt/live/ptest.example.org/fullchain.pem;
  ssl_certificate_key /usr/local/etc/letsencrypt/live/ptest.example.org/privkey.pem;

  fastcgi_pass   unix:/var/run/php-fpm.sock;

Once all edits are complete, test the configuration file with

  # nginx -t

There should be no errors. Nginx will start, but any browsing will cause errors because we are not done yet.

MySQL and Reinitializing MySQL

Mysql server should be running. Check with:

  # service mysql-server status

#If it is not started, and if # service mysql-server start does not start mysql, you may have to reinitialize the database. Here are the steps to reinitialize MySQL:

  # cd /var/db/mysql
  # rm -rf *
  # ls -al
  total 17
  drwxr-x---   2 mysql mysql  2 May 10 14:39 .
  drwxr-xr-x  20 root  wheel 23 May 10 13:18 ..
  # cd ..
  # /usr/local/libexec/mysqld --initialize --user=mysql --datadir=/var/db/mysql
  2025-05-10T18:45:44.533006Z 0 [System] [MY-015017] [Server] MySQL Server Initialization - start.
  2025-05-10T18:45:44.535872Z 0 [Warning] [MY-010915] [Server] 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be  merged with strict mode in a future release.
  2025-05-10T18:45:44.536021Z 0 [System] [MY-013169] [Server] /usr/local/libexec/mysqld (mysqld 8.4.3) initializing of server in progress as process 46752
  mysqld: Error on delete of '/var/db/mysql/auto.cnf' (OS errno 2 - No such file or directory)
  2025-05-10T18:45:44.788423Z 0 [Warning] [MY-010107] [Server] World-writable config file '/var/db/mysql/auto.cnf' has been removed.
  2025-05-10T18:45:44.792948Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
  2025-05-10T18:45:46.641922Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
  2025-05-10T18:45:49.300164Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: ++mysqlSecretPassword123++
  2025-05-10T18:45:52.231932Z 0 [System] [MY-015018] [Server] MySQL Server Initialization - end.
  # 

Note the new root password above. You will need it to log in again.

MYSQL – Create the Phorge Databases

It is now time bring up the phorge databases and tables.

First, create the needed credentials:

  # mysql -u root -p    (use the password from the initialization step above if needed)
  root@localhost [(none)]> create user 'phorge'@'localhost' identified by 'MyPhorgeSecretPw';
  root@localhost [(none)]> create database phorge character set utf8mb4 collate utf8mb4_general_ci;
  root@localhost [(none)]> grant all privileges on `phabricator\_%`.* to 'phorge'@'localhost';   NOTE THE USE OF BACKTICKS HERE. 
  root@localhost [(none)]> grant all privileges on phorge.* to 'phorge'@'localhost';
  root@localhost [(none)]> flush privileges;
  root@localhost [(none)]> quit;

Then try bring up the phorge. The following command loads everything into MySQL:

  $T/bin/storage upgrade --user phorge --password MyPhorgeSecretPw –-force

You should see:

  Loading quickstart template onto "localhost:3306"...
  Applying patch "phabricator:db.paste" to host "localhost:3306"...
  Applying patch "phabricator:20190523.myisam.01.documentfield.sql" to host "localhost:3306"...
  Applying patch "phabricator:20190718.paste.01.edge.sql" to host "localhost:3306"...
  Applying patch "phabricator:20190718.paste.02.edgedata.sql" to host "localhost:3306"...
  Applying patch "phabricator:20190718.paste.03.paste.sql" to host "localhost:3306"...
  ...  Many more statements
  Completed applying all schema adjustments.
   ANALYZE  Analyzing tables...                                                 
  Done.                                                                         
   ANALYZED  Analyzed 525 table(s).
  root@ptest:~ #

Final Alignments and Startup

A number of parameters have to align correctly. Follow these notes to align parameters where needed:


  * nginx root ‘/usr/local/lib/php/phorge/webroot’
       The package puts the code here, there is no need to clone from phorge website.

  * MySQL user ‘phorge’@’localhost’

  * local.json  mysql.user  phorge     (Note: not phorge@ptest.example.org)

  * MySQL user phorge permissions      (Note: see above)

Stop any running nginx and mysql services:

  # service mysql-server stop
  
  # service nginx stop

Ensure all phorge elements in /etc/rc.conf are enabled:

  nginx_enable="YES"
  mysql_enable="YES"
  php_fpm_enable="YES"
  phd_enable="YES"
  git_daemon_enable="YES"
  pygments_enabled="YES"

sshd Alignments

Copy the sshd_config.phorge.5555 file below into /etc/ssh and edit for for local interface IP addr.

Ensure that /usr/libexec/phorge-ssh-hook.sh exists See file below And ensure owner of root:wheel. Script should be mode 755

LOCAL.JSON

Update local.json with the copy below.

Also change “phabricator.base-uri” to local system URI:

  "phabricator.base-uri": "https://ptest.example.org",

Make sure all directories exist and have the correct permissions.

  # mkdir -p /var/phorge/repos
  # mkdir -p /var/phorge/repos
  # chown -R www:www /var/phorge

Set an alias for root in /etc/aliases

  root:  jpb@localhost

and run newaliases as root.

Initial Phorge Startup

Start up each service by hand one at a time and fix any errors:

  # /usr/sbin/sshd -f /etc/ssh/sshd_config.phorge.5555

  # service nginx start

  # service mysql-server start
       (Check with service mysql-server status.)

  # service php_fpm start
       (Should say conf test is successful.)

  # service phd start
       (Should note launching daemons.)

  # chown www:www /var/run/php-fpm.sock
  
  # ps -ax | grep ssh
       (Should be two sshd daemons running.)

PHORGE SHOULD BE UP!
  
TIME SENSITIVE - DO IMMEDIATELY

  Web to https://ptest.example.org
  
  Should see the initial phorge admin screen.
  
  Set the admin account and password!
  
  Then use the ph.sh script to stop everything.
Figure 2: Completed Phorge Instance

Figure 2: Completed Phorge Instance