Setting Up a Phorge Instance
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
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