I am running a Kimai Time Tracking instance on my own server. But do I really need my own server for that?
Motivation
Recently I had to set up an on-premise server for my employer and kept being involved in some webdevelopment and sysadmin work. It was a lot of fun and I started running several private virtual private servers. They were useful for testing before deploying at my employers server and also enabled me to shift my own workflow from the cloud of mega companies to my self hosted apps.
But there is a problem. Sysadmin is a profession not because it is easy, but because errors appear and they need solving. Ultimately, I grew a bit tired of managing my own stack after I am done managing the stack at work. But I want to keep some apps, so let’s try and move to free (when small scale) alternatives.
Heroku offers this in the form of hosting apps in a containerized environment with add-ons like databases and whatnot. So they can give me the Php server and mysql database Kimai needs. The free database is limited to 5mb, if that’s not enough, I will just host it myself. But at least I am free of taking care of the Php stuff.
Whatever, take me to the TL;DR already
Prerequisites
SPOILER ALERT - don’t install any php locally, it’s a waste of time
In order to package and test the app locally, we have to install PHP and composer first
sudo add-apt-repository ppa:ondrej/php
sudo apt install php7.4 unzip
In order to actually run Kimai, we needs some more php extensions
sudo apt install php7.4-zip php7.4-gd php7.4-intl php7.4-xml php7.4-xsl php7.4-mbstring php7.4-curl
Time to install composer, some copy pasta for the terminal from this page
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar $USER/.local/bin/composer
Installation
Now, create an empty project on Heroku using the web interface and clone Kimai into your playground. After that, add the Heroku remote to your local Kimai git repo. Also tell Heroku that its a php app and add the Procfile to use apache pointing at the public directory of Kimai.
git clone -b 1.9 --depth 1 https://github.com/kevinpapst/kimai2.git && cd kimai2
heroku git:remote -a herokuappname
heroku buildpacks:set heroku/php
echo "web: vendor/bin/heroku-php-apache2 public" > Procfile
Since we will also need a mysql DB, we can continue to configure the Heroku stack. using the website, go to extensions and search for it, or enter the command
heroku addons:create jawsdb:kitefin
Since Kimai expects it’s DB string under the environment name DATABASE_URL, create both, the local .env file and also edit the system variable in the Heroku project settings.
nano .env
DATABASE_URL=mysql://lalala:blalal.eu-west-0.rds.amazonaws.com:3306/mydb
While we are at it, also supply an actual app-secret to Symfoni
APP_SECRET=Whatever
Another important part is to prevent user registration, at least for me it is not needed. Random people can register and see my customers and projects, no thank you. To stop it, we need to create a config file that doesn’t exist by default
echo "kimai:\n user:\n registration: false" > config/packages/local.yaml
git add -f config/packages/local.yaml
And continue with the Kimai installation.
composer install --no-dev --optimize-autoloader
This failed for me with some bug, so I just tried to deploy to Heroku instead
git branch master
git fetch origin --unshallow
git push heroku master
During the first installation I hit a limit on the free database. There were too many migrations going on and it choked. After waiting a minute I tried again and it completed the remaining migrations. So keep that in mind, the install command will fail once. Next we create the admin account.
heroku run bash
bin/console kimai:install -n; sleep 60; bin/console kimai:install -n
bin/console kimai:create-user username admin@example.com ROLE_SUPER_ADMIN
Dump and restore the old database (optional)
mysqldump -u root -pmypasswd kimaidb > kimai_dump.sql
then get the connection info for jawsdb from the database string and just use a local mysql client to upload the old data
mysql://NEWUSER:NEWPASS@NEWHOST:3306/NEWDATABASE
mysql -h NEWHOST -u NEWUSER -pNEWPASS NEWDATABASE < kimai_dump.sql
After this, the app didn’t work anymore. I expected a Version mismatch (the lxc container I dumped from is a old) so I just ran an update on Kimai using Heroku bash
heroku run bash
bin/console kimai:update
Which showed me some table I already head from the original installation was keeping it from applying correct migrations. Connect again using mysql client and
drop table kimai2_invoices;
and now the update went through without a problem. The app is working and my old records are present. やったー!
Wrapping up
One thing to note is, Heroku keeps defaulting to plain http, but I could install an extension to Firefox, Instead of installing an extension, the redirect can be achieved by the modifying the htaccess file in the public folder. there is already a redirect rule prepared but commented. It didn’t work for me, as on Heroku the load balanced does the ssl. But a small change, using different environment variable, and it’s working perfectly.https everywhere
, that always tries to use https, if available.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://myappname.herokuapp.com/$1 [R,L]
</IfModule>
I also tried setting up ssl with Heroku using a custom domain and Cloudflares origin cert, but, that is only available for paid dynos. Bummer. But I could get an “ok” appname at Heroku, so it doesn’t really matter. One thing less to worry about, actually.
Also noteworthy is, there is a limit on the free database of 5mb, here’s an sql snippet to see the current usage, or just use a GUI like HeidiSQL to check the usage
select
s.schema_name
,sp.grantee user
,cast(round(sum(coalesce(t.data_length + t.index_length, 0)) / 1024 / 1024, 3) as char) db_size_mb
,sp.has_insert
from
information_schema.schemata s
inner join
information_schema.tables t on s.schema_name = t.table_schema
inner join (
select
spi.grantee
,spi.table_schema
,max(
case
when spi.privilege_type = 'INSERT' then 1
else 0
end
) has_insert
from
information_schema.schema_privileges spi
group by
spi.grantee
,spi.table_schema
) sp on s.schema_name = sp.table_schema
group by
s.schema_name
,sp.grantee
,sp.has_insert;
Updating
Clone the project from Heroku to the development machine, add the original Source repository, pull and push.
heroku git:clone -a mykimai
git remote add -f github https://github.com/kevinpapst/kimai2.git
git pull github master
git push heroku master
Run the Kimai2 updater in the Heroku environment.
heroku run bash
bin/console kimai:update
That’s it! easy!
TL;DR
まだです。