form wonderfall

This commit is contained in:
Dryusdan 2017-12-04 22:18:49 +01:00
commit b89166bdff
10 changed files with 565 additions and 0 deletions

71
Dockerfile.12.0 Normal file
View File

@ -0,0 +1,71 @@
FROM wonderfall/nginx-php:7.1
ARG NEXTCLOUD_VERSION=12.0.2
ARG GPG_nextcloud="2880 6A87 8AE4 23A2 8372 792E D758 99B9 A724 937A"
ENV UID=991 GID=991 \
UPLOAD_MAX_SIZE=10G \
APC_SHM_SIZE=128M \
OPCACHE_MEM_SIZE=128 \
MEMORY_LIMIT=512M \
CRON_PERIOD=15m \
CRON_MEMORY_LIMIT=1g \
TZ=Etc/UTC \
DB_TYPE=sqlite3 \
DOMAIN=localhost
RUN apk -U upgrade \
&& apk add -t build-dependencies \
gnupg \
tar \
build-base \
autoconf \
automake \
pcre-dev \
libtool \
samba-dev \
&& apk add \
libressl \
ca-certificates \
libsmbclient \
tzdata \
&& pecl install \
smbclient \
apcu \
redis \
&& echo "extension=smbclient.so" > /php/conf.d/smbclient.ini \
&& echo "extension=redis.so" > /php/conf.d/redis.ini \
&& mkdir /nextcloud \
&& cd /tmp \
&& NEXTCLOUD_TARBALL="nextcloud-${NEXTCLOUD_VERSION}.tar.bz2" \
&& wget -q https://download.nextcloud.com/server/releases/${NEXTCLOUD_TARBALL} \
&& wget -q https://download.nextcloud.com/server/releases/${NEXTCLOUD_TARBALL}.sha512 \
&& wget -q https://download.nextcloud.com/server/releases/${NEXTCLOUD_TARBALL}.asc \
&& wget -q https://nextcloud.com/nextcloud.asc \
&& echo "Verifying both integrity and authenticity of ${NEXTCLOUD_TARBALL}..." \
&& CHECKSUM_STATE=$(echo -n $(sha512sum -c ${NEXTCLOUD_TARBALL}.sha512) | tail -c 2) \
&& if [ "${CHECKSUM_STATE}" != "OK" ]; then echo "Warning! Checksum does not match!" && exit 1; fi \
&& gpg --import nextcloud.asc \
&& FINGERPRINT="$(LANG=C gpg --verify ${NEXTCLOUD_TARBALL}.asc ${NEXTCLOUD_TARBALL} 2>&1 \
| sed -n "s#Primary key fingerprint: \(.*\)#\1#p")" \
&& if [ -z "${FINGERPRINT}" ]; then echo "Warning! Invalid GPG signature!" && exit 1; fi \
&& if [ "${FINGERPRINT}" != "${GPG_nextcloud}" ]; then echo "Warning! Wrong GPG fingerprint!" && exit 1; fi \
&& echo "All seems good, now unpacking ${NEXTCLOUD_TARBALL}..." \
&& tar xjf ${NEXTCLOUD_TARBALL} --strip 1 -C /nextcloud \
&& update-ca-certificates \
&& apk del build-dependencies \
&& rm -rf /var/cache/apk/* /tmp/* /root/.gnupg
COPY rootfs /
RUN chmod +x /usr/local/bin/* /etc/s6.d/*/* /etc/s6.d/.s6-svscan/*
VOLUME /data /config /apps2 /nextcloud/themes
EXPOSE 8888
LABEL description="A server software for creating file hosting services" \
nextcloud="Nextcloud v${NEXTCLOUD_VERSION}" \
maintainer="Wonderfall <wonderfall@targaryen.house>"
CMD ["run.sh"]

249
README.md Normal file
View File

@ -0,0 +1,249 @@
## wonderfall/nextcloud
[![](https://images.microbadger.com/badges/version/wonderfall/nextcloud.svg)](http://microbadger.com/images/wonderfall/nextcloud "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/wonderfall/nextcloud.svg)](http://microbadger.com/images/wonderfall/nextcloud "Get your own image badge on microbadger.com")
[![](https://images.microbadger.com/badges/version/wonderfall/nextcloud:daily.svg)](https://microbadger.com/images/wonderfall/nextcloud:daily "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/wonderfall/nextcloud:daily.svg)](https://microbadger.com/images/wonderfall/nextcloud:daily "Get your own image badge on microbadger.com")
[![](https://images.microbadger.com/badges/version/wonderfall/nextcloud:11.0.svg)](https://microbadger.com/images/wonderfall/nextcloud:11.0 "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/wonderfall/nextcloud:11.0.svg)](https://microbadger.com/images/wonderfall/nextcloud:11.0 "Get your own image badge on microbadger.com")
![](https://s32.postimg.org/69nev7aol/Nextcloud_logo.png)
**This image was made for my own use and I have no intention to make this official. Support won't be regular so if there's an update, or a fix, you can open a pull request. Any contribution is welcome, but please be aware I'm very busy currently. Before opening an issue, please check if there's already one related. Also please use Github instead of Docker Hub, otherwise I won't see your comments. Thanks.**
### Features
- Based on Alpine Linux.
- Bundled with nginx and PHP 7.1 (wonderfall/nginx-php image).
- Automatic installation using environment variables.
- Package integrity (SHA512) and authenticity (PGP) checked during building process.
- Data and apps persistence.
- OPCache (opcocde), APCu (local) installed and configured.
- system cron task running.
- MySQL, PostgreSQL (server not built-in) and sqlite3 support.
- Redis, FTP, SMB, LDAP, IMAP support.
- GNU Libiconv for php iconv extension (avoiding errors with some apps).
- No root processes. Never.
- Environment variables provided (see below).
### Tags
- **latest** : latest stable version. (12.0)
- **12.0** : latest 12.0.x version (stable)
- **11.0** : latest 11.0.x version (old stable)
- **10.0** : latest 10.0.x version (old stable) (unmaintained)
- **9.0** : latest 9.0.x version. (old stable) (unmaintained)
- **daily** : latest code (daily build).
Other tags than `daily` are built weekly. For security reasons, you should occasionally update the container, even if you have the latest version of Nextcloud.
### Build-time variables
- **NEXTCLOUD_VERSION** : version of nextcloud
- **GNU_LIBICONV_VERSION** : version of GNU Libiconv
- **GPG_nextcloud** : signing key fingerprint
### Environment variables
- **UID** : nextcloud user id *(default : 991)*
- **GID** : nextcloud group id *(default : 991)*
- **UPLOAD_MAX_SIZE** : maximum upload size *(default : 10G)*
- **APC_SHM_SIZE** : apc memory size *(default : 128M)*
- **OPCACHE_MEM_SIZE** : opcache memory size in megabytes *(default : 128)*
- **MEMORY_LIMIT** : php memory limit *(default : 512M)*
- **CRON_PERIOD** : time interval between two cron tasks *(default : 15m)*
- **CRON_MEMORY_LIMIT** : memory limit for PHP when executing cronjobs *(default : 1024m)*
- **TZ** : the system/log timezone *(default : Etc/UTC)*
- **ADMIN_USER** : username of the admin account *(default : none, web configuration)*
- **ADMIN_PASSWORD** : password of the admin account *(default : none, web configuration)*
- **DOMAIN** : domain to use during the setup *(default : localhost)*
- **DB_TYPE** : database type (sqlite3, mysql or pgsql) *(default : sqlite3)*
- **DB_NAME** : name of database *(default : none)*
- **DB_USER** : username for database *(default : none)*
- **DB_PASSWORD** : password for database user *(default : none)*
- **DB_HOST** : database host *(default : none)*
Don't forget to use a **strong password** for the admin account!
### Port
- **8888** : HTTP Nextcloud port.
### Volumes
- **/data** : Nextcloud data.
- **/config** : config.php location.
- **/apps2** : Nextcloud downloaded apps.
- **/nextcloud/themes** : Nextcloud themes location.
- **/php/session** : php session files.
### Database
Basically, you can use a database instance running on the host or any other machine. An easier solution is to use an external database container. I suggest you to use MariaDB, which is a reliable database server. You can use the official `mariadb` image available on Docker Hub to create a database container, which must be linked to the Nextcloud container. PostgreSQL can also be used as well.
### Setup
Pull the image and create a container. `/mnt` can be anywhere on your host, this is just an example. Change `MYSQL_ROOT_PASSWORD` and `MYSQL_PASSWORD` values (mariadb). You may also want to change UID and GID for Nextcloud, as well as other variables (see *Environment Variables*).
```
docker pull wonderfall/nextcloud:10.0 && docker pull mariadb:10
docker run -d --name db_nextcloud \
-v /mnt/nextcloud/db:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=supersecretpassword \
-e MYSQL_DATABASE=nextcloud -e MYSQL_USER=nextcloud \
-e MYSQL_PASSWORD=supersecretpassword \
mariadb:10
docker run -d --name nextcloud \
--link db_nextcloud:db_nextcloud \
-v /mnt/nextcloud/data:/data \
-v /mnt/nextcloud/config:/config \
-v /mnt/nextcloud/apps:/apps2 \
-v /mnt/nextcloud/themes:/nextcloud/themes \
-e UID=1000 -e GID=1000 \
-e UPLOAD_MAX_SIZE=10G \
-e APC_SHM_SIZE=128M \
-e OPCACHE_MEM_SIZE=128 \
-e CRON_PERIOD=15m \
-e TZ=Etc/UTC \
-e ADMIN_USER=mrrobot \
-e ADMIN_PASSWORD=supercomplicatedpassword \
-e DOMAIN=cloud.example.com \
-e DB_TYPE=mysql \
-e DB_NAME=nextcloud \
-e DB_USER=nextcloud \
-e DB_PASSWORD=supersecretpassword \
-e DB_HOST=db_nextcloud \
wonderfall/nextcloud:10.0
```
You are **not obliged** to use `ADMIN_USER` and `ADMIN_PASSWORD`. If these variables are not provided, you'll be able to configure your admin acccount from your browser.
**Below you can find a docker-compose file, which is very useful!**
Now you have to use a **reverse proxy** in order to access to your container through Internet, steps and details are available at the end of the README.md. And that's it! Since you already configured Nextcloud through setting environment variables, there's no setup page.
### ARM-based devices
You will have to build yourself using an Alpine-ARM image, like `orax/alpine-armhf:edge`.
### Configure
In the admin panel, you should switch from `AJAX cron` to `cron` (system cron).
### Update
Pull a newer image, then recreate the container as you did before (*Setup* step). None of your data will be lost since you're using external volumes. If Nextcloud performed a full upgrade, your apps could be disabled, enable them again.
### Docker-compose
I advise you to use [docker-compose](https://docs.docker.com/compose/), which is a great tool for managing containers. You can create a `docker-compose.yml` with the following content (which must be adapted to your needs) and then run `docker-compose up -d nextcloud-db`, wait some 15 seconds for the database to come up, then run everything with `docker-compose up -d`, that's it! On subsequent runs, a single `docker-compose up -d` is sufficient!
#### Docker-compose file
Don't copy/paste without thinking! It is a model so you can see how to do it correctly.
```
nextcloud:
image: wonderfall/nextcloud
links:
- nextcloud-db:nextcloud-db # If using MySQL
- solr:solr # If using Nextant
- redis:redis # If using Redis
environment:
- UID=1000
- GID=1000
- UPLOAD_MAX_SIZE=10G
- APC_SHM_SIZE=128M
- OPCACHE_MEM_SIZE=128
- CRON_PERIOD=15m
- TZ=Europe/Berlin
- ADMIN_USER=admin # Don't set to configure through browser
- ADMIN_PASSWORD=admin # Don't set to configure through browser
- DOMAIN=localhost
- DB_TYPE=mysql
- DB_NAME=nextcloud
- DB_USER=nextcloud
- DB_PASSWORD=supersecretpassword
- DB_HOST=nextcloud-db
volumes:
- /mnt/nextcloud/data:/data
- /mnt/nextcloud/config:/config
- /mnt/nextcloud/apps:/apps2
- /mnt/nextcloud/themes:/nextcloud/themes
# If using MySQL
nextcloud-db:
image: mariadb:10
volumes:
- /mnt/nextcloud/db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=supersecretpassword
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=supersecretpassword
# If using Nextant
solr:
image: solr:6-alpine
container_name: solr
volumes:
- /mnt/docker/solr:/opt/solr/server/solr/mycores
entrypoint:
- docker-entrypoint.sh
- solr-precreate
- nextant
# If using Redis
redis:
image: redis:alpine
container_name: redis
volumes:
- /mnt/docker/redis:/data
```
You can update everything with `docker-compose pull` followed by `docker-compose up -d`.
### How to configure Redis
Redis can be used for distributed and file locking cache, alongside with APCu (local cache), thus making Nextcloud even more faster. As PHP redis extension is already included, all you have to is to deploy a redis server (you can do as above with docker-compose) and bind it to nextcloud in your config.php file :
```
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.local' => '\OC\Memcache\APCu',
'redis' => array(
'host' => 'redis',
'port' => 6379,
 ),
```
### How to configure Nextant
You will have to deploy a Solr server, I've shown an example above with docker-compose. Once Nextant app is installed, go to "additional settings" in your admin pannel and use http://solr:8983/solr as "Adress of your Solr Servlet". There you go. You may however experience the same issue as mine : https://github.com/nextcloud/server/pull/3160 (let's hope there'll be at least a backport...).
### Tip : how to use occ command
There is a script for that, so you shouldn't bother to log into the container, set the right permissions, and so on. Use `docker exec -ti nexcloud occ command`.
### Reverse proxy
Of course you can use your own solution to do so! nginx, Haproxy, Caddy, h2o, there's plenty of choices and documentation about it on the Web.
Personally I'm using nginx, so if you're using nginx, there are two possibilites :
- nginx is on the host : get the Nextcloud container IP address with `docker inspect nextcloud | grep IPAddress\" | head -n1 | grep -Eo "[0-9.]+" `. But whenever the container is restarted or recreated, its IP address can change. Or you can bind Nextcloud HTTP port (8888) to the host (so the reverse proxy can access with `http://localhost:8888` or whatever port you set), but in this case you should consider using a firewall since it's also listening to `http://0.0.0.0:8888`.
- nginx is in a container, things are easier : you can link nextcloud container to an nginx container so you can use `proxy_pass http://nextcloud:8888`. If you're interested, I provide a nginx image available on Docker Hub : `wonderfall/boring-nginx`, and it comes with a script called `ngxproxy`, which does all the magic after asking you a few questions. Otherwise, an example of configuration would be :
```
server {
listen 8000;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 4430 ssl http2;
server_name example.com;
ssl_certificate /certs/example.com.crt;
ssl_certificate_key /certs/example.com.key;
include /etc/nginx/conf/ssl_params.conf;
client_max_body_size 10G; # change this value it according to $UPLOAD_MAX_SIZE
location / {
proxy_pass http://nextcloud:8888;
include /etc/nginx/conf/proxy_params;
}
}
```
Headers are already sent by the container, including HSTS, so there's no need to add them again. **It is strongly recommended to use Nextcloud through an encrypted connection (HTTPS).** [Let's Encrypt](https://letsencrypt.org/) provides free SSL/TLS certificates (trustworthy!).

View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 0

6
rootfs/etc/s6.d/cron/run Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh
while true; do
php -d memory_limit=<CRON_MEMORY_LIMIT> -f /nextcloud/cron.php
sleep <CRON_PERIOD>
done

View File

@ -0,0 +1,75 @@
server {
listen 8888;
root /nextcloud;
fastcgi_buffers 64 4K;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
location / {
rewrite ^ /index.php$uri;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
include /nginx/conf/fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass unix:/php/run/php-fpm.sock;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_read_timeout 1200;
}
location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}
location ~* \.(?:css|js)$ {
try_files $uri /index.php$uri$is_args$args;
add_header Cache-Control "public, max-age=7200";
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
access_log off;
}
location ~* \.(?:svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
access_log off;
}
}

View File

@ -0,0 +1,4 @@
extension=apcu.so
apc.enabled=1
apc.shm_size=<APC_SHM_SIZE>
apc.ttl=7200

View File

@ -0,0 +1,8 @@
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.fast_shutdown=1
opcache.memory_consumption=<OPCACHE_MEM_SIZE>
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60

2
rootfs/usr/local/bin/occ Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
su-exec $UID:$GID php -d memory_limit=<MEMORY_LIMIT> -f /nextcloud/occ $@

View File

@ -0,0 +1,38 @@
#!/bin/sh
sed -i -e "s/<APC_SHM_SIZE>/$APC_SHM_SIZE/g" /php/conf.d/apcu.ini \
-e "s/<OPCACHE_MEM_SIZE>/$OPCACHE_MEM_SIZE/g" /php/conf.d/opcache.ini \
-e "s/<CRON_MEMORY_LIMIT>/$CRON_MEMORY_LIMIT/g" /etc/s6.d/cron/run \
-e "s/<CRON_PERIOD>/$CRON_PERIOD/g" /etc/s6.d/cron/run \
-e "s/<MEMORY_LIMIT>/$MEMORY_LIMIT/g" /usr/local/bin/occ \
-e "s/<UPLOAD_MAX_SIZE>/$UPLOAD_MAX_SIZE/g" /nginx/conf/nginx.conf /php/etc/php-fpm.conf \
-e "s/<MEMORY_LIMIT>/$MEMORY_LIMIT/g" /php/etc/php-fpm.conf
# Put the configuration and apps into volumes
ln -sf /config/config.php /nextcloud/config/config.php &>/dev/null
ln -sf /apps2 /nextcloud &>/dev/null
# Create folder for php sessions if not exists
if [ ! -d /data/session ]; then
mkdir -p /data/session;
fi
echo "Updating permissions..."
for dir in /nextcloud /data /config /apps2 /var/log /php /nginx /tmp /etc/s6.d; do
if $(find $dir ! -user $UID -o ! -group $GID|egrep '.' -q); then
echo "Updating permissions in $dir..."
chown -R $UID:$GID $dir
else
echo "Permissions in $dir are correct."
fi
done
echo "Done updating permissions."
if [ ! -f /config/config.php ]; then
# New installation, run the setup
/usr/local/bin/setup.sh
else
occ upgrade
fi
exec su-exec $UID:$GID /bin/s6-svscan /etc/s6.d

109
rootfs/usr/local/bin/setup.sh Executable file
View File

@ -0,0 +1,109 @@
#!/bin/sh
# Nextcloud
##########################
#source setup/functions.sh # load our functions
#source /etc/mailinabox.conf # load global vars
CONFIGFILE=/config/config.php
# Create an initial configuration file.
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1)
cat > $CONFIGFILE <<EOF;
<?php
\$CONFIG = array (
'datadirectory' => '/data',
"apps_paths" => array (
0 => array (
"path" => "/nextcloud/apps",
"url" => "/apps",
"writable" => false,
),
1 => array (
"path" => "/apps2",
"url" => "/apps2",
"writable" => true,
),
),
'memcache.local' => '\OC\Memcache\APCu',
'instanceid' => '$instanceid',
);
?>
EOF
# Create an auto-configuration file to fill in database settings
# when the install script is run. Make an administrator account
# here or else the install can't finish.
adminpassword=$(dd if=/dev/urandom bs=1 count=40 2>/dev/null | sha1sum | fold -w 30 | head -n 1)
cat > /nextcloud/config/autoconfig.php <<EOF;
<?php
\$AUTOCONFIG = array (
# storage/database
'directory' => '/data',
'dbtype' => '${DB_TYPE:-sqlite3}',
'dbname' => '${DB_NAME:-nextcloud}',
'dbuser' => '${DB_USER:-nextcloud}',
'dbpass' => '${DB_PASSWORD:-password}',
'dbhost' => '${DB_HOST:-nextcloud-db}',
'dbtableprefix' => 'oc_',
EOF
if [[ ! -z "$ADMIN_USER" ]]; then
cat >> /nextcloud/config/autoconfig.php <<EOF;
# create an administrator account with a random password so that
# the user does not have to enter anything on first load of ownCloud
'adminlogin' => '${ADMIN_USER}',
'adminpass' => '${ADMIN_PASSWORD}',
EOF
fi
cat >> /nextcloud/config/autoconfig.php <<EOF;
);
?>
EOF
echo "Starting automatic configuration..."
# Execute ownCloud's setup step, which creates the ownCloud database.
# It also wipes it if it exists. And it updates config.php with database
# settings and deletes the autoconfig.php file.
(cd /nextcloud; php index.php &>/dev/null)
echo "Automatic configuration finished."
# Update config.php.
# * trusted_domains is reset to localhost by autoconfig starting with ownCloud 8.1.1,
# so set it here. It also can change if the box's PRIMARY_HOSTNAME changes, so
# this will make sure it has the right value.
# * Some settings weren't included in previous versions of Mail-in-a-Box.
# * We need to set the timezone to the system timezone to allow fail2ban to ban
# users within the proper timeframe
# * We need to set the logdateformat to something that will work correctly with fail2ban
# Use PHP to read the settings file, modify it, and write out the new settings array.
CONFIG_TEMP=$(/bin/mktemp)
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $CONFIGFILE
<?php
include("/config/config.php");
//\$CONFIG['memcache.local'] = '\\OC\\Memcache\\Memcached';
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
\$CONFIG['logtimezone'] = '$TZ';
\$CONFIG['logdateformat'] = 'Y-m-d H:i:s';
echo "<?php\n\\\$CONFIG = ";
var_export(\$CONFIG);
echo ";";
?>
EOF
sed -i "s/localhost/$DOMAIN/g" /config/config.php
chown -R $UID:$GID /config /data
# Enable/disable apps. Note that this must be done after the ownCloud setup.
# The firstrunwizard gave Josh all sorts of problems, so disabling that.
# user_external is what allows ownCloud to use IMAP for login. The contacts
# and calendar apps are the extensions we really care about here.
if [[ ! -z "$ADMIN_USER" ]]; then
occ app:disable firstrunwizard
fi