This image containerizes Kanboard kanban webapp and the background worker using Beanstalk along with its php dependencies to track tasks for an individual or small group.
latest tag is annotated as multiarch so pulling without specifying any architecture tags should fetch the correct image for your architecture. Same goes for any of the version tags.
non-x86_64 images used to contain the embedded qemu-user-static binary which has been redundant for a while, and is being deprecated starting with our Alpine Linux v3.22 base-image release, see qemu-user-static or the more recent binfmt instead for running multi-arch containers.
If you want to run images built for other architectures on the same host (e.g. a x86_64 machine), you will need to have the specific binary format support configured on your host machine before running the image (otherwise you get an exec format error). Here's how,
For recent images, we can use tonistiigi's binfmtimage to register binary execution support for the target architecture, like the following,
However, that image (see qemu-user-static) seems to have fallen behind in updates, and with newer images the binfmt method is preferable.
Now images built for other architectures will also be executable. This is optional though, without the above, you can still run the image that is specifically made to support your host architecture.
FQDN of your kanboard host including subpath, (kanboard is available at path /kanboard/). Required to set the first-run URL in database. (Relevant issue)
KANBOARD_BACKUPDIR
/config/www
Path to directory where current installation backup is generated during an update.
KANBOARD_UPDATE
unset
If set to true, will reinstall kanboard at ${KANBOARDDIR} even if a previous installation exists. Useful if you're persisting your whole installation. Also generates a backup of the previous installation at ${KANBOARD_BACKUPDIR}/kanboard-VERSION-YYYY-MM-DD.zip
KANBOARD_PRESERVEFILES
data,plugins,config.php
Comma-separated list of files/dirs that are excluded from deletion during an update.
KANBOARD_CRONFILE
/etc/crontabs/root
Cron-registry file of a user (defaults to root). This file is modified to contain the periodic task script that will be executed.
KANBOARD_CRONJOB
/defaults/cronjob.sh
File to execute on cron invocation.
KANBOARD_CRONTIME
0 8 * * *
cron-formatted invocation timings.
PHPDIR
/etc/php82
Root directory for php config files.
PHPCONFDIR
/etc/php82/conf.d
Directory for php config snippets.
PHP_MAX_EXECUTION_TIME
30
php.ini value for max_execution_time. (Only set if file does not exist)
PHP_MAX_FILE_UPLOADS
20
php.ini value for max_file_uploads. (Only set if file does not exist)
PHP_MEMORY_LIMIT
128M
php.ini value for memory_limit. (Only set if file does not exist)
PHP_POST_MAX_SIZE
16M
php.ini value for post_max_size. (Only set if file does not exist)
PHP_UPLOAD_MAX_FILESIZE
16M
php.ini value for upload_max_filesize. (Only set if file does not exist)
PHP_OPCACHE_ENABLE_CLI
0
php.ini value for opcache.enable_cli. (Only set if file does not exist.) (since 8.2.22)
PHP_SKIP_PERMFIX
unset
If set to a non-empty-string value (e.g. 1), skips fixing permissions for php configuration files/directories. (since 8.2.27)
PHP_ADD_DEFAULT_PHPINFO
unset
If set to true and no php scripts exist inside ${WEBDIR}, a default phpinfo.php is copied into it. Useful for testing. (since 8.2.22) Previously PHP_SKIP_DEFAULT_PHPINFO, enabled by default.
PHP_PERMFIX_WEBDIR
unset
If set to true, ensures files inside ${WEBDIR} are owned/accessible by ${S6_USER}. (since 8.2.22)
PHPFPMCONF
/etc/php82/php-fpm.conf
php-fpm main config file.
PHPFPMDIR
/etc/php82/php-fpm.d
Directory for php-fpm config snippets.
PHPFPMSOCK
/var/run/php-fpm.sock
php-fpm socket file location.
PHPFPM_ARGS
-F
Customizable arguments passed to php-fpm service at runtime.
PHPFPM_ERROR_LOG
/proc/self/fd/2
php-fpm.conf value for error_log. (Only set if file does not exist)
PHPFPM_LOG_LEVEL
error
php-fpm.conf value for log_level. (Only set if file does not exist) (since 8.2.22)
PHPFPM_CATCH_WORKERS_OUTPUT
no
php-fpm.d/www.conf value for catch_workers_output. (Only set if file does not exist) (since 8.2.22)
PHPFPM_CLEAR_ENV
yes
php-fpm.d/www.conf value for clear_env. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM
dynamic
php-fpm.d/www.conf value for pm. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM_MAX_CHILDREN
5
php-fpm.d/www.conf value for pm.max_children. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM_MAX_REQUESTS
500
php-fpm.d/www.conf value for pm.max_requests. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM_MAX_SPARE_SERVERS
3
php-fpm.d/www.conf value for pm.max_spare_servers. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM_MIN_SPARE_SERVERS
1
php-fpm.d/www.conf value for pm.min_spare_servers. (Only set if file does not exist) (since 8.2.22)
PHPFPM_PM_START_SERVERS
2
php-fpm.d/www.conf value for pm.start_servers. (Only set if file does not exist) (since 8.2.22)
ROOTDIR
/config
Root directory for nginx configs or webserver files.
NGINXDIR
/config/nginx
Default directory for nginx configurations.
CONFSDIR
/config/nginx/conf.d
For shared configuration snippets.
LOGSDIR
/config/nginx/logs
For logs (especially if run as a non-root user). (since 1.28.0)
SITESDIR
/config/nginx/http.d
For webserver host configuration files.
STREAMSDIR
/config/nginx/stream.d
For stream configuration files. (Optional, requires stream module to be enabled in configurations).
TMPDIR
/config/nginx/tmp
For temporary or buffer files e.g. client body, cgi or proxy caches (especially if run as a non-root user). (since 1.28.0)
NGINX_NO_HTTP
unset
Set to 'true' to disable default http(80) conf file, has no effect if custom site-confs exist.
NGINX_NO_HTTPS
unset
Set to 'true' to disable default https(443) conf file, and default self-signed certificate generation on first run.
KEYDIR
/config/keys
Default certificate/privatekey location.
PKEYFILE
/config/keys/private.key
Default path to privatekey. (Make sure site-confs reflect the same)
CERTFILE
/config/keys/certificate.crt
Default path to certificate. (Make sure site-confs reflect the same)
For serving files, e.g. either static HTML or dynamic scripts i.e. with PHP.
NGINX_ADD_DEFAULT_INDEX
unset
If set to true and no files exist inside ${WEBDIR}, a static index.html is copied into it. Useful for testing. (since 1.26.2) Previously NGINX_SKIP_DEFAULT_INDEX, enabled by default.
NGINX_SKIP_PERMFIX
unset
If set to a non-empty-string value (e.g. 1), skips fixing permissions for nginx configuration files/directories. (since 1.26.2_20250225)
NGINX_PERMFIX_WEBDIR
unset
If set to true, ensures files inside ${WEBDIR} are owned/accessible by ${S6_USER}. (since 1.26.2)
NGINX_ARGS
-F
Customizable arguments passed to nginx service at runtime. (since 1.28.0)
CROND_ARGS
-f -S -l 5
Customizable arguments passed to cron daemon.
CROND_CONF
/etc/crontabs
Default file to read configurations from. By default this file enables scripts from /etc/periodic to be executed.
SKIP_CRON
unset
Set to true to skip starting cron daemon.
S6_NEEDED_PACKAGES
empty string
Space-separated list of extra APK packages to install on start. E.g. "curl git tzdata"
PUID
1000
Id of ${S6_USER}.
PGID
1000
Group id of ${S6_USER}.
S6_USER
alpine
(Preset) Default non-root user for services to drop privileges to.
S6_USERHOME
/home/alpine
(Preset) HOME directory for ${S6_USER}.
Did you know?
You can check your own UID/GID by running the command id in a terminal.
Also,
By itself, only SQLITE Database storage included. Checkout alpine-mysql or alpine-postgresql to configure your own database server as another container.
Kanboard is located at the endpoint /kanboard/, with the webapp at /config/www/kanboard/. If not exists, (or if KANBOARD_UPDATE is set) the release included in the image gets unzipped at this path.
Configuration file is at /config/www/kanboard/config.php, edit or remount this with your own. A sample is provided in /defaults, this gets copied when no such file exists before service is started.
You can either mount only the data and plugins directories to persist between updates, or mount the whole /config directory to have better (but manual) control over your installation.
For first-time-right setups provide correct KANBOARD_URL to be set in the database. (Relevant issue)
Kanboard source is located at /opt/kanboard/kanboard-${VERSION}.zip.
This snippet can be used to reverse-proxy the service using NGINX.
upstreamproxy_kanboard{serveryour.host.local:<kanboard-port>fail_timeout=5;}## the following goes inside a server blocklocation/kanboard{return301/kanboard/;}location/kanboard/{auth_basicoff;proxy_set_headerX-Forwarded-Server$host;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_headerX-Forwarded-Proto$scheme;proxy_set_headerHost$http_host;proxy_set_headerX-Real-IP$remote_addr;proxy_read_timeout300;proxy_connect_timeout300;proxy_passhttp://proxy_kanboard/kanboard/;}
Feel free to clone (or fork) the repository and customize it for your own usage, build the image for yourself on your own systems, and optionally, push it to your own public (or private) repository.
Before we clone the /repository, we must have Git, GNU make, and Docker (optionally, with buildx plugin for multi-platform images) setup on the machine. Also, for multi-platform annotations, we might require enabling experimental features of Docker.
To create the image for your architecture, run the build and test target with
makebuildtest
For building an image that targets another architecture, it is required to specify the ARCH parameter when building. e.g.
makebuildtestARCH=aarch64
makebuildtestARCH=armhf
makebuildtestARCH=armv7l
makebuildtestARCH=i386
makebuildtestARCH=ppc64le
makebuildtestARCH=riscv64
makebuildtestARCH=s390x
makebuildtestARCH=x86_64
Build Parameters
All images have a few common build parameters that can be customized at build time, like
ARCH
The target architecture to build for. Defaults to host architecture, auto-detected at build-time if not specified. Also determines if binfmt support is required before build or run and runs the regbinfmt (or inbinfmt for recent images) target automatically. Possible values could be aarch64, armhf, armv7l, or x86_64.
BUILDDATE
The date of the build. Can be used to create separate tags for images. (format: yyyymmdd)
DOCKERFILE
The dockerfile to use for build. Defaults to the file Dockerfile, but if per-arch dockerfiles exist, (e.g. for x86_64 the filename would be Dockerfile_x86_64) that is used instead.
TESTCMD
The command to run for testing the image after build. Runs in a bash shell.
VERSION
The version of the app/tool, may need to be preset before starting the build (e.g. for binaries from github releases), or extracted from the image after build (e.g. for APK or pip packages).
REGISTRY
The registry to push to, defaults to the Docker Hub Registry (docker.io) or any custom registry that is set via docker configurations. Does not need to be changed for local or test builds, but to override, either pass it by setting an environment variable, or with every make command.
ORGNAME
The organization (or user) name under which the image repositories exist, defaults to woahbase. Does not need to be changed for local or test builds, but to override, either pass it by setting an environment variable, or with every make command.
The image may also require custom parameters (like binary architecture). Before you build, check the makefile for a complete list of parameters to see what may (or may not) need to be set.
BuildX and Self-signed certificates
If you're using a private registry (a-la docker distribution server) with self-signed certificates, that fail to validate when pulling/pushing images. You will need to configure buildx to allow insecure access to the registry. This is configured via the config.toml file. A sample is provided in the repository, make sure to replace YOUR.PRIVATE.REGISTRY with your own (include port if needed).
Running the image creates a container and either starts a service (for service images) or provides a shell (can be either a root-shell or usershell) to execute commands in, depending on the image. We can run the image with
makerun
But if we just need a root-shell in the container without any fance pre-tasks (e.g. for debug or to test something bespoke), we can run bash in the container with --entrypoint /bin/bash. This is wrapped in the makefile as
makeshell
Nothing vs All vs Run vs Shell
By default, if make is run without any arguments, it calls the target all. In our case this is usually mapped to the target run (which in turn may be mapped to shell).
There may be more such targets defined as per the usage of the image. Check the makefile for more information.
If the build and test steps finish without any error, and we want to use the image on other machines, it is the next step push the image we built to a container image repository (like /hub), for that, run the push target with
makepush
If the built image targets another architecture then it is required to specify the ARCH parameter when pushing. e.g.
makepushARCH=aarch64
makepushARCH=armhf
makepushARCH=armv7l
makepushARCH=i386
makepushARCH=ppc64le
makepushARCH=riscv64
makepushARCH=s390x
makepushARCH=x86_64
Pushing Multiple Tags
With a single make push, we are actually pushing 3 tags of the same image, e.g. for x86_64 architecture, they're namely
alpine-kanboard:x86_64
The actual image that is built.
alpine-kanboard:x86_64_${version}
It is expected that the application is versioned when built or packaged, it can be specified in the tag, this makes pulling an image by tag possible. Usually this is obtained from the parameter VERSION, which by default, is set by calling a function to extract the version string from the package installed in the container, or from github releases. Can be skipped with the parameter SKIP_VERSIONTAG to a non-empty string value like 1.
alpine-kanboard:x86_64_${version}_${builddate}
When building multiple versions of the same image (e.g. for providing fixes or revisions), this ensures that a more recent push does not fully replace a previously pushed image. This way, although the architecture and version tags are replaced, it is possible to roll back to the previously built image by build date (format yyyymmdd). This value is obtained from the BUILDDATE parameter, and if not essential, can be skipped by setting the parameter SKIP_BUILDDATETAG to a non-empty string value like 1.
Pushing To A Private Registry
If you want to push the image to a custom registry that is not pre-configured on your system, you can set the REGISTRY variable either on the build environment, or as a makefile parameter, and that will be used instead of the default Docker Hub repository. Make sure to have push access set up before you actually push, and include port if needed. E.g.
For single architecture images, the above should suffice, the built image can be used in the host machine, and on other machines that have the same architecture too, i.e. after a push.
But for use-cases that need to support multiple architectures, there's a couple more things that need to be done. We need to create (or amend if already created beforehand) a manifest for the image(s) that we built, then annotate it to map the images to their respective architectures. And for our three tags created above we need to do it thrice.
Did you know?
We can inspect the manifest of any image by running
Assuming we built the images for all supported architectures, to facilitate pulling the correct image for the architecture, we can create/amend the latest manifest and annotate it to map the tags :aarch64, :armhf, :armv7l, :i386, :ppc64le, :riscv64, :s390x, :x86_64 to the tag :latest by running
makeannotate_latest
How it works
First we create or amend the manifest with the tag latest
Next, to facilitate pulling images by version, we create/amend the image-version manifest and annotate it to map the tags :aarch64_${version}, :armhf_${version}, :armv7l_${version}, :i386_${version}, :ppc64le_${version}, :riscv64_${version}, :s390x_${version}, :x86_64_${version} to the tag :${version} by running
makeannotate_version
How it works
First we create or amend the manifest with the tag ${version}
Then, (optionally) we create/amend the ${version}_${builddate} manifest and annotate it to map the tags :aarch64_${version}_${builddate}, :armhf_${version}_${builddate}, :armv7l_${version}_${builddate}, :i386_${version}_${builddate}, :ppc64le_${version}_${builddate}, :riscv64_${version}_${builddate}, :s390x_${version}_${builddate}, :x86_64_${version}_${builddate} to the tag :${version}_${builddate} by running
makeannotate_date
How it works
First we create or amend the manifest with the tag ${version}_${builddate}
Maintained (or sometimes a lack thereof?) by WOAHBase.
Helped you save a bit of your valuable time and effort that is best spent doing stuff you actually enjoy? If you have some pocket-change to spare, kindly consider helping out.