Roland's homepage

My random knot in the Web

Installing OpenProject on FreeBSD

At work we want to run OpenProject. But since this a ruby-on-rails app and installation on Windows is not supported, I volunteered to set up a dedicated FreeBSD server for it.

Server

The server install was a basic FreeBSD 10 install done via the memory stick image. On the harddisk I created a 50 GB root partition and 8 GB swap. The rest of the disk was made into a data partition.

Since you will generally log into a server via ssh, you can use long random passwords for the accounts on the server. I use a script to read data from the random device and convert it into something readable with base64. This will yield passwords like “yX5iD8FSpUdgy0Pu” or “4JYMnC4zxXsK_pZR”. Keep them in an encrypted file on your workstation so you can copy/paste them into ssh.

After the usual configuration, I installed an up to date ports tree using the built-in svnlite.

> su
# rm -rf /usr/ports/*
# svnlite checkout https://svn0.eu.FreeBSD.org/ports/head /usr/ports

After this initial checkout, keeping the ports tree up to date is simple;

# svnlite update /usr/ports

With the ports tree in place, the following programs have been installed;

  • vim from /usr/ports/editors/vim
  • rsync from /usr/ports/net/rsync
  • git from /usr/ports/devel/git
  • nginx from /usr/ports/www/nginx
  • ruby 2.1 from /usr/ports/lang/ruby21
  • ruby-gems from /usr/ports/devel/ruby-gems
  • postgresql 9.3 server from /usr/ports/databases/postgresql93-server
  • gcc 4.7 from /usr/ports/lang/gcc/
  • sqlite 3.8 from /usr/ports/databases/sqlite3
  • npm 1.4 from /usr/ports/www/npm
  • ImageMagick 6.8 from /usr/ports/graphics/ImageMagick-nox11

The installation of all ports goes much the same;

# cd /usr/ports/net/rsync
# make install clean

Before installing ruby-gems, the following needs to be set in /etc/make.conf;

DEFAULT_VERSIONS= ruby=2.1

Otherwise installing ruby-gems will start installing the default ruby 1.9.

After installing gcc 4.7, the g++47 and gcc executables are linked to g++ and gcc respectively.

When installing npm you should not choose node-devel (that gives v0.11, we need v0.10).

The ImageMagick port depends on ghostscript. In the ghostscript I disabled all the printer models and left in the different bitmap and vector formats.

Preparing OpenProject

The data and software for openproject will be installed on the /data parition.

When postgresql was installed, a postgresql user named pgsql was created automatically. I set a long randomly generated password for this user. But you can set * as the password and use /usr/sbin/nologin as the shell. This prevents ordinary logins as this user. But root can still use su -m to assume this identity.

We will now create the directories for the database and for openproject.

# mkdir /data/pgsql
# chown pgsql:pgsql /data/pgsql
# chmod 700 /data/pgsql

For openproject we will first create a user, and then create a directory owned by that user;

# pw useradd openproject -c "OpenProject user" -d /data/openproject -h 0
# mkdir /data/openproject
# chown openproject:openproject /data/openproject
# chmod 755 /data/openproject

The password for the openproject user is set to a long random one.

After ruby and ruby-gems were installed, I installed bundler;

# gem install bundler --version '>=1.5.1'
Fetching: bundler-1.6.2.gem (100%)
Successfully installed bundler-1.6.2
1 gem installed
Installing ri documentation for bundler-1.6.2...
Installing RDoc documentation for bundler-1.6.2...

The following settings were added to /etc/rc.conf to enable the postgresql server;

postgresql_enable="YES"
postgresql_data="/data/pgsql"
postgresql_flags="-w -s -m fast"
postgresql_initdb_flags="--encoding=utf-8 --lc-collate=C"
postgresql_class="default"
postgresql_profiles=""

Additionally, we need to initialze the database;

# service postgresql initdb
The files belonging to this database system will be owned by user "pgsql".
This user must also own the server process.

The database cluster will be initialized with locales
COLLATE:  C
CTYPE:    en_US.UTF-8
MESSAGES: en_US.UTF-8
MONETARY: en_US.UTF-8
NUMERIC:  en_US.UTF-8
TIME:     en_US.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /data/pgsql ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
creating configuration files ... ok
creating template1 database in /data/pgsql/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects descriptions ... ok
creating collations ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok
syncing data to disk ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

   /usr/local/bin/postgres -D /data/pgsql
or
    /usr/local/bin/pg_ctl -D /data/pgsql -l logfile start

After these changes the service was started using the new FreeBSD way;

# service postgresql start

According to the openproject installation instructions, I created the databases (the password used here is obviously not the real one);

# su - pgsql
$ createdb -O pgsql openproject
$ createdb -O pgsql openproject_test
$ createdb -O pgsql openproject_development
$ psql openproject
psql (9.3.4)
Type "help" for help.

openproject=# create role openproject login encrypted password 'W_PlqQbNHD28cm1c' noinherit valid until 'infinity';
CREATE ROLE
openproject-# \q
$ exit

Now it is time to install OpenProject itself. From here on, commands prefixed with $ are carried out as the user openproject.

# cd /data
# git clone https://github.com/opf/openproject.git
Cloning into '.'...
remote: Reusing existing pack: 139758, done.
remote: Counting objects: 36, done.
remote: Compressing objects: 100% (35/35), done.
remote: Total 139794 (delta 12), reused 0 (delta 0)
Receiving objects: 100% (139794/139794), 46.37 MiB | 1.25 MiB/s, done.
Resolving deltas: 100% (100180/100180), done.
Checking connectivity... done.
# chown -R openproject:openproject openproject/

Now we need to check out the latest release;

# su - openproject
$ git checkout stable
$ exit

Next is installing the necessary configuration files from the repository that I created for it. The deploy program checks if a file is out of date and installs it when necessary.

# cd /home/rsmith/openproject-setup/
# ./deploy install
File 'op/Gemfile.plugins' was successfully installed as '/data/openproject/Gemfile.plugins'.
File 'op/configuration.yml' was successfully installed as '/data/openproject/config/configuration.yml'.
File 'op/database.yml' was successfully installed as '/data/openproject/config/database.yml'.

In the /data/openproject directory we create a Gemfile.plugins to contain the plugins that we want to use;

# Required for each plug-in
gem "openproject-plugins", :git => "https://github.com/opf/openproject-plugins.git", :branch => "stable"

# Allow adding documents to projects
gem 'openproject-documents', :git => 'https://github.com/opf/openproject-documents.git', :branch => 'stable'

The openproject framework needs some javascript tools as well;

# npm -g install bower
# su - openproject
$ npm install
$ bower install

Next we need to install some gems;

# cd /data/openproject
# bundle install --without mysql --without mysql2

From the gems that are installed it seems that openproject is running the “thin” webserver.

Since we already created the databases earlier, we can now seed it, and create the secret token for the session store, and precompile the assets;

# su - openproject
$ env RAILS_ENV=production bundle exec rake db:create:all
$ env RAILS_ENV=production bundle exec rake generate_secret_token
$ env RAILS_ENV=production bundle exec rake db:migrate db:seed
$ env RAILS_ENV=production bundle exec rake assets:precompile

Now we should be able to run the rails server;

$ env RAILS_ENV=production bundle exec rails server
require 'rails/all'... 0.672s
Bundler.require... 2.000s
=> Booting Thin
=> Rails 3.2.18 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Application.initialize!... [deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
/usr/local/lib/ruby/gems/2.1/gems/svg-graph-1.0.5/lib/SVG/Graph/Graph.rb:3: warning: class variable access from toplevel
3.992s
>> Thin web server (v1.5.1 codename Straight Razor)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:3000, CTRL+C to stop

This will start the server listening on port 3000.

A production server only produces the dynamic content. The site also requires static content like /assets/favicon-ecd67ccc017223d35746687df7b5de89.ico. This file is located in /data/openproject/public/assets/favicon-ecd67ccc017223d35746687df7b5de89.ico. So the root directory for the static content webserver is /data/openproject/public. We will be using the nginx web server. Normally the server is run as the user www. nginx requires a place to store its log files. This is configured as the directory /var/log/nginx;

# mkdir /var/log/nginx
# chown www:www /var/log/nginx

For testing purposes, the nginx server can now be started with;

# service nginx onestart
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.

With this, the system seems to work. Yay!

To start OpenProject at boot, a FreeBSD rc script was written;

!/bin/sh
# vim:fileencoding=utf-8:ft=sh
# Restart commands for OpenProject
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# Created: 2014-05-25 19:22:47 +0200
# $Date: 2014-05-25 22:22:27 +0200 $
# $Revision: bef6b1b $

# PROVIDE: openproject
# REQUIRE: postgresql nginx
# KEYWORD: shutdown

. /etc/rc.subr

name="openproject"
rcvar=openproject_enable
start_cmd="op_start"
stop_cmd="op_stop"
PID=/tmp/pids/server.pid

op_start()
{
    CMD="cd /data/openproject; bundle exec rails server -d -e production -P $PID"
    if [ ! -e $PID ]; then
        su -m openproject -c "$CMD"
        echo "Started OpenProject server."
    else
        echo "OpenProject already running."
    fi
}

op_stop()
{
    if [ -e $PID ]; then
        kill `cat $PID`
    else
        echo "OpenProject not running."
    fi
}

load_rc_config $name
run_rc_command "$1"

And the services that need starting are listed in rc.conf;

# file: rc.conf
# vim:fileencoding=utf-8
# Restart commands for openproject.vfi.local
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# Created: 2014-05-10 22:20:05 +0200
# Modified: $Date: 2014-05-15 02:08:32 +0200 $

hostname="openproject.vfi.local"
ifconfig_em0="DHCP"
sshd_enable="YES"
powerd_enable="YES"
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"
postgresql_enable="YES"
postgresql_data="/data/pgsql"
postgresql_flags="-w -s -m fast"
postgresql_initdb_flags="--encoding=utf-8 --lc-collate=C"
postgresql_class="default"
postgresql_profiles=""
powerd_enable="YES"
powerd_flags="-n hiadaptive -a hiadaptive"
sendmail_enable="NONE"
sysvipc_enable="YES"
nginx_enable="YES"
openproject_enable="YES"

Using the web interface I logged in as administrator, and created a new administrator ID for myself, and non-administrator user-IDs for the other users.


←  Preventing the ~/Desktop direcory Modifying a FreeBSD port  →