This post is far overdue, especially since these products are EoL in 2024. Atlassian never officially supported hosting JIRA or Confluence on FreeBSD, nor did they officially support openJDK. Here's how I got both of them working on the latest FreeBSD 13.2.

💡
These steps were performed on a system that was running an older version of JIRA and Confluence. I tried to capture all steps needed for a new install (with the exception of the MySQL configuration). If something isn't working, I'd recommend referencing the logs in the following locations:
- /usr/local/share/confluence/logs/catalina.out
- /usr/local/confluence/confluence-data/logs/atlassian-confluence.log
- /usr/local/share/jira/logs/catalina.out
- /usr/local/jira/log/atlassian-jira.log

Start by obtaining the latest installers from Atlassian. In this guide, I will be using Confluence 8.5.1 and JIRA 9.11.0. I used the tar.gz files.

curl -OL https://product-downloads.atlassian.com/software/jira/downloads/atlassian-jira-software-9.11.0.tar.gz
curl -OL https://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-8.5.1.tar.gz

I chose the following locations for the files:
- Confluence install: /usr/local/share/confluence
- Confluence Home: /usr/local/confluence
- JIRA install: /usr/local/share/jira
- JIRA Home: /usr/local/jira

mkdir /usr/local/share/confluence /usr/local/confluence /usr/local/share/jira /usr/local/jira

Generate service accounts:

pw useradd -n jira -u 71 -c "JIRA Service Account" -s /bin/sh -d /usr/local/jira -w random
pw useradd -n confluence -u 72 -c "Confluence Service Account" -s /bin/sh -d /usr/local/confluence -w random

Extract the tar.gz files to their respective locations:

tar -xzf atlassian-confluence-8.5.1.tar.gz -C /usr/local/share/confluence
tar -xzf atlassian-jira-software-9.11.0.tar.gz -C /usr/local/share/jira

Change ownership of both installation and home directories:

chown -R confluence /usr/local/share/confluence /usr/local/confluence
chown -R jira /usr/local/share/jira /usr/local/jira

Install the openJDK packages. You'll need version 11 for JIRA and 17 for Confluence:

pkg install openjdk11 openjdk11-jre openjdk17 openjdk17-jre

Neither product comes with the MySQL driver. I'm using MySQL8 so I obtained the files from here: https://dev.mysql.com/downloads/connector/j/8.0.html
You can download either format since you only need to unpack the archive, and move a single file into your JIRA/Confluence install. In this example I'm using the tar.gz file:

cd ~
curl -o mysql_connector.tar.gz -L https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-j-8.1.0.tar.gz
mkdir mysql_connector
tar -xzf mysql_connector.tar.gz -C mysql_connector
cp mysql_connector/mysql-connector-j-<YOUR VERSION HERE>.jar /usr/local/share/confluence/confluence/WEB-INF/lib/
cp mysql_connector/mysql-connector-j-<YOUR VERSION HERE>.jar /usr/local/share/jira/lib/

Create RC scripts for each service. Make sure you update the paths for the <product>_install and <product_home variables to match the locations used to install the applications and setup the user's home paths.

vi /usr/local/etc/rc.d/jira
#!/bin/sh

#
# PROVIDE: jira
# REQUIRE: DAEMON 
# KEYWORD: shutdown

. /etc/rc.subr

load_rc_config jira

JAVA_HOME="/usr/local/openjdk11"
JRE_HOME="/usr/local/openjdk11-jre"
JIRA_INSTALL="/usr/local/share/jira"
JIRA_HOME="/usr/local/jira"

jira_enable=${jira_enable:-"NO"}
jira_user=${jira_user:-"jira"}

name=jira
rcvar=jira_enable

procname="java"
pidfile="/var/run/jira.pid"

start_cmd="jira_start"
stop_cmd="jira_stop"

jira_start()
{
 su -l ${jira_user} -c "export JAVA_HOME=${JAVA_HOME};export JRE_HOME=${JRE_HOME};export JIRA_HOME=${JIRA_HOME};${JIRA_INSTALL}/bin/catalina.sh start || err 1 'Error triggering JIRA startup'"
}

jira_stop()
{
 su -l ${jira_user} -c "export JAVA_HOME=${JAVA_HOME};export JRE_HOME=${JRE_HOME};export JIRA_HOME=${JIRA_HOME};${JIRA_INSTALL}/bin/catalina.sh stop 10 -force || err 1 'Error triggering JIRA shutdown'"
}

run_rc_command "$1"
vi /usr/local/etc/rc.d/confluence
#!/bin/sh

#
# PROVIDE: confluence
# REQUIRE: DAEMON 
# KEYWORD: shutdown

. /etc/rc.subr

load_rc_config confluence

JAVA_HOME="/usr/local/openjdk17"
JRE_HOME="/usr/local/openjdk17-jre"
CONFLUENCE_INSTALL="/usr/local/share/confluence"

confluence_enable=${confluence_enable:-"NO"}
confluence_user=${confluence_user:-"confluence"}

name=confluence
rcvar=confluence_enable

procname="java"
pidfile="/var/run/confluence.pid"

start_cmd="confluence_start"
stop_cmd="confluence_stop"

confluence_start()
{
 su -l ${confluence_user} -c "export JAVA_HOME=${JAVA_HOME};export JRE_HOME=${JRE_HOME};${CONFLUENCE_INSTALL}/bin/catalina.sh start || err 1 'Error triggering CONFLUENCE startup'"
}

confluence_stop()
{
 su -l ${confluence_user} -c "export JAVA_HOME=${JAVA_HOME};export JRE_HOME=${JRE_HOME};${CONFLUENCE_INSTALL}/bin/catalina.sh stop 10 -force || err 1 'Error triggering CONFLUENCE shutdown'"
}

run_rc_command "$1"

Make both RC scripts executable:

chmod +x /usr/local/etc/rc.d/jira /usr/local/etc/rc.d/confluence

For JIRA only, I had to update the bash path in a few files:

cd /usr/local/share/jira/bin
sed -i '.bak' 's|#!/bin/bash|#!/usr/local/bin/bash|' catalina.sh config.sh start-jira.sh stop-jira.sh

Nginx Reverse Proxy

These next few steps are only required if you place JIRA/Confluence behind a reverse proxy like Nginx.

For Confluence, modify the /usr/local/share/confluence/conf/server.xml file to be aware of the reverse proxy:

<Server port="8000" shutdown="SHUTDOWN">
    <Service name="Tomcat-Standalone">
        <Connector port="8090" connectionTimeout="20000" redirectPort="8443"
                   maxThreads="48" maxPostSize="16777216" minSpareThreads="10"
                   enableLookups="false" acceptCount="10" URIEncoding="UTF-8"
                   protocol="org.apache.coyote.http11.Http11NioProtocol"
                   scheme="https" secure="true" proxyName="<ENTER YOUR DOMAIN HERE.SUBDOMAIN.ROOT>" proxyPort="443"/>
        
        <Engine name="Standalone" defaultHost="localhost">
            <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="false" startStopThreads="4">
                <Context path="" docBase="../confluence" reloadable="false" useHttpOnly="true">
                    <!-- Logging configuration for Confluence is specified in confluence/WEB-INF/classes/log4j.properties -->
                    <Manager pathname=""/>
                    <Valve className="org.apache.catalina.valves.StuckThreadDetectionValve" threshold="60"/>

                    <!-- http://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_Log_Valve -->
                    <Valve className="org.apache.catalina.valves.AccessLogValve"
                           directory="logs"
                           maxDays="30"
                           pattern="%t %{X-AUSERNAME}o %I %h %r %s %Dms %b %{Referer}i %{User-Agent}i"
                           prefix="conf_access_log"
                           requestAttributesEnabled="true"
                           rotatable="true"
                           suffix=".log"
                    />

                    <!-- http://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Remote_IP_Valve -->
                    <Valve className="org.apache.catalina.valves.RemoteIpValve" />
                </Context>

                <Context path="${confluence.context.path}/synchrony-proxy" docBase="../synchrony-proxy"
                         reloadable="false" useHttpOnly="true">
                    <Valve className="org.apache.catalina.valves.StuckThreadDetectionValve" threshold="60"/>
                </Context>
            </Host>
        </Engine>
    </Service>
</Server>

You'll want to update the <ENTER YOUR DOMAIN HERE...> part to match your domain in Nginx. Configuring Nginx is outside the scope of this guide, but Atlassian has detailed information on how to perform that task.

JIRA is a similar edit, but I needed two connectors to enable the application link between the two products:

<?xml version="1.0" encoding="utf-8"?>

<Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

    <Service name="Catalina">

        <!-- Relaxing chars because of JRASERVER-67974 -->
        <Connector port="8080" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"
                   maxThreads="150" minSpareThreads="25" connectionTimeout="20000" enableLookups="false"
                   maxHttpHeaderSize="8192" protocol="HTTP/1.1" useBodyEncodingForURI="true" redirectPort="8443"
                   acceptCount="100" disableUploadTimeout="true" bindOnInit="false"/>

        <Connector port="8082" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"
                   maxThreads="150" minSpareThreads="25" connectionTimeout="20000" enableLookups="false"
                   maxHttpHeaderSize="8192" protocol="HTTP/1.1" useBodyEncodingForURI="true" redirectPort="8443"
                   acceptCount="100" disableUploadTimeout="true" bindOnInit="false" secure="true" scheme="https"
                   proxyName="<ENTER YOUR DOMAIN HERE.SUBDOMAIN.ROOT>" proxyPort="443"/>

        <Engine name="Catalina" defaultHost="localhost">
            <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

                <Context path="" docBase="${catalina.home}/atlassian-jira" reloadable="false" useHttpOnly="true">
                    <Resource name="UserTransaction" auth="Container" type="javax.transaction.UserTransaction"
                              factory="org.objectweb.jotm.UserTransactionFactory" jotm.timeout="60"/>
                    <Manager pathname=""/>
                    <JarScanner scanManifest="false"/>
                    <Valve className="org.apache.catalina.valves.StuckThreadDetectionValve" threshold="120" />
                </Context>

            </Host>
            <Valve className="org.apache.catalina.valves.AccessLogValve"
                   pattern="%a %{jira.request.id}r %{jira.request.username}r %t &quot;%m %U%{sanitized.query}r %H&quot; %s %b %D &quot;%{sanitized.referer}r&quot; &quot;%{User-Agent}i&quot; &quot;%{jira.request.assession.id}r&quot;"/>
        </Engine>
    </Service>
</Server>

Again, same as before, update the <ENTER YOUR DOMAIN HERE...> part to match your Nginx configuration.

At this point, you should be able to start both services with service start jira and service start confluence.