This guide will walk through installing dotCMS from scratch using current best practices. (CentOS)
- Installing dotCMS
- PostgreSQL Configuration
- dotCMS Configuration
- (>5.3.0) ElasticSearch Configuration
- Apache Reverse-Proxy Configuration
- SSL Using Certbot
- Monit Monitoring
There is also a terminal command reference at the bottom of this document. Workarounds & Fixes.
This is optional, but highly recommended.
Run: sysctl -w fs.file-max=100000
Edit: /etc/security/limits.conf
dotcms hard nofile 10000
dotcms soft nofile 10000
yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum -y install epel-release
yum -y install certbot httpd mod_proxy_html mod_ssl wget curl nano htop mc iptables-services setroubleshoot setools ant java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-headless.x86_64 postgresql10 postgresql10-server nmap monit
yum -y update
reboot
https://www.elastic.co/guide/en/elasticsearch/reference/7.7/rpm.html#rpm-repo
mkdir -p /opt/dotcms && cd /opt/dotcms
wget http://static.dotcms.com/versions/dotcms_5.3.0.tar.gz
wget https://github.com/x0rsw1tch/dotcms-starters/raw/master/dotcms-5.2.1_minimal.zip
Note: dotCMS provides blank starter archives for versions 5.2.8 and higher
I don't know whick ones are for which version 😒
groupadd dotcms
useradd dotcms -g dotcms
passwd dotcms
/usr/pgsql-10/bin/postgresql10-setup initdb
systemctl enable postgresql-10
systemctl start postgresql-10
su postgres
psql
Create dotcms user
CLI: createuser -P dotcms
You will be prompted for password
CREATE DATABASE "dotcms" WITH OWNER = dotcms;
GRANT ALL ON DATABASE "dotcms" TO dotcms;
ALTER ROLE dotcms WITH SUPERUSER;
CREATE USER 'dotcms'@'localhost';
SET PASSWORD FOR 'dotcms'@'localhost' = PASSWORD('****CHANGEME****');
CREATE DATABASE `dotcms` DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_general_ci;
GRANT ALL PRIVILEGES ON `dotcms`.`*` TO 'dotcms'@'localhost';
nano /var/lib/pgsql/10/data/pg_hba.conf
host all all 127.0.0.1/32 password
systemctl restart postgresql
cd /opt/dotcms
tar -zxvf dotcms_5.3.0.tar.gz
mkdir -p /var/run/dotcms
chown dotcms:dotcms /var/run/dotcms
mkdir -p plugins/com.dotcms.config/ROOT/bin
mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/META-INF
mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/conf
mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/log4j
mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/classes
mkdir -p plugins/com.dotcms.config/ROOT/bin mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/webapps/ROOT/META-INF mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/conf mkdir -p plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/webapps/ROOT/WEB-INF
cp dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/classes/db.properties plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/classes/
cp dotserver/tomcat-8.5.32/conf/server.xml plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/conf/
cp bin/startup.sh plugins/com.dotcms.config/ROOT/bin/
NOTE: Versions older than 5.3.0 don't use
db.properties
instead copycontext.xml
to the config plugin:
cp dotserver/tomcat-8.5.32/webapps/ROOT/META-INF/context.xml plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/META-INF/
cp dotserver/tomcat-8.0.18/webapps/ROOT/META-INF/context.xml plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/webapps/ROOT/META-INF/ cp dotserver/tomcat-8.0.18/conf/server.xml plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/conf/ cp bin/startup.sh plugins/com.dotcms.config/ROOT/bin/
cp dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/log4j/log4j2-example.xml plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/log4j/log4j2.xml
nano plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/log4j/log4j2.xml
<Logger name="com.dotcms.content.elasticsearch" level="debug">
<AppenderRef ref="generic"/>
</Logger>
nano plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/classes/db.properties
##Postgres default configuration
driverClassName=org.postgresql.Driver
jdbcUrl=jdbc:postgresql://localhost/DB_NAME
username=DB_USERNAME
password=DB_PASSWORD
connectionTestQuery=SELECT 1
nano plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/META-INF/context.xml
NOTE: Be sure to comment out the H2 section, and uncomment the PostgreSQL section
<Resource name="jdbc/dotCMSPool" auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost/dotcms"
username="dotcms" password="****CHANGEME****" maxTotal="60" maxIdle="10" maxWaitMillis="60000"
removeAbandonedOnBorrow="true" removeAbandonedOnMaintenance="true" removeAbandonedTimeout="60" logAbandoned="true"
timeBetweenEvictionRunsMillis="30000" validationQuery="SELECT 1" testOnBorrow="true" testWhileIdle="true" />
nano plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"
secure="true" proxyPort="443" scheme="https" />
Add to bottom of
<Host>
with the rest of the Valves
<Valve className="org.apache.catalina.valves.RemoteIpValve" internalProxies="0:0:0:0:0:0:0:1" remoteIpHeader="x-forwarded-for" proxiesHeader="x-forwarded-by" protocolHeader="X-Forwarded-Proto" />
proxyPort="80"
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
mv dotserver/tomcat-8.5.32/webapps/ROOT/starter.zip dotserver/tomcat-8.5.32/webapps/ROOT/starter-vanilla.zip
mv dotcms-5.2.1_minimal.zip plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT
mv dotserver/tomcat-8.0.18/webapps/ROOT/starter.zip dotserver/tomcat-8.0.18/webapps/ROOT/starter-vanilla.zip mv dotcms-4.3.3_minimal.zip plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.0.18/webapps/ROOT
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
STARTER_DATA_LOAD=/dotcms-5.2.1_minimal.zip
More Information: Custom Starter, Minimal Starter
nano plugins/com.dotcms.config/ROOT/bin/startup.sh
-Xmx4G
More Information: Memory Config
export CATALINA_PID="/var/run/dotcms/dotcms.pid"
mkdir /opt/dotcms-assets
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
ASSET_REAL_PATH=/opt/dotcms-assets
More Information: Asset Directory
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
cache.contentletcache.size=20000
cache.blockdirectivecache.size=3600
cache.categoryparentscache.size=90000
cache.csscache.size=10000
cache.fileassetmetadatacache.size=6000
cache.foldercache.size=6000
cache.htmlpagecache.size=24000
cache.identifier404cache.size=9000
cache.rulescache.size=5000
cache.tagsbyinodecache.size=4000
cache.taginodesbyinodecache.size=6000
cache.taginodecache.size=4000
cache.velocitycache.size=5000
cache.virtuallinkscache.size=3500
cache.shortyidcache.size=5000
cache.workflowstepcache.size=4000
Recommended if the server is not using clustered hosts and have multiple+separate instances on the same network
nano plugins/com.dotcms.config/conf/dotcms-config-cluster-ext.properties
AUTOWIRE_CLUSTER_TRANSPORT=false
AUTOWIRE_CLUSTER_ES=false
DIST_INDEXATION_ENABLED=true
ES_INDEX_REPLICAS=0
AUTOWIRE_MANAGE_ES_REPLICAS=false
For dotcms 5.3.0 and higher (adjust based on your ES instance configuration)
ES_HOSTNAME=127.0.0.1
ES_PORT=9200
ES_TLS_ENABLED=false
ES_PROTOCOL=http
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
DOTCMS_LOGGING_HOME=/opt/dotcms/dotserver/tomcat-8.5.32/logs
TAIL_LOG_LOG_FOLDER=/opt/dotcms/dotserver/tomcat-8.5.32/logs
nano plugins/com.dotcms.config/ROOT/dotserver/tomcat-8.5.32/webapps/ROOT/WEB-INF/log4j/log4j2.xml
Use the contents of this file
As root
nano ~/.bashrc
alias dottail='tail -fn100 /opt/dotcms/dotserver/tomcat-8.5.32/logs/catalina.out -fn100 /opt/dotcms/dotserver/tomcat-8.5.32/logs/dotcms.log -fn100 /opt/dotcms/dotserver/tomcat-8.5.32/logs/dotcms-velocity.log'
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
VELOCITY_INCLUDE_ALLOWED_EXTENSIONS=css,htm,html,js,json,txt,svg,md
Only needed when unauthenticated users submit data to the back-end that go to a Content Type
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
CONTENT_APIS_ALLOW_ANONYMOUS=WRITE
Allow via
$workflowtool
(undocumented, but in code)
WORKFLOW_TOOL_ALLOW_FRONT_END_SAVING=true
This should only be done in the following scenarios:
- You require a front-end user login and:
- Not using native dotCMS users, instead using a Content Type
- Don't want to use an OSGI plugin to handle logins
- Want to be able to set users in Velocity
VELOCITY_PREVENT_SETTING_USER_ID=false
nano plugins/com.dotcms.config/conf/dotmarketing-config-ext.properties
CMS_SHARED_HOST
andCMS_DEFAULT_HOST
should match to sites setup within dotcms.CMS_SHARED_ASSETS
should map to the shared hosts with an alias that matches to remote front-end requests, useful for hosts that don't yet have a public DNS records.
CMS_SHARED_HOST=shared.example.com
CMS_DEFAULT_HOST=www.example.com
CMS_SHARED_ASSETS=dev.example.com
chown -R dotcms:dotcms /opt/dotcms
nano /etc/profile.d/java_home.sh
export JAVA_HOME=/usr/lib/jvm/jre-openjdk
(Logout and back in)
bin/deploy-plugins.sh
cd /opt/dotcms
- Make changes.
bin/deploy-plugins.sh
More Information: Configuration Properties
As of dotCMS 5.3.0 ElasticSearch is no longer part of the binary releases. ElasticSearch must be installed separately. This portion of the guide assumes you will be running a single dotCMS instance with a single ElasticSearch instance.
- CentOS: https://www.elastic.co/guide/en/elasticsearch/reference/7.7/rpm.html#rpm-repo
- Debian: https://www.elastic.co/guide/en/elasticsearch/reference/7.7/deb.html#deb-repo
Set the following configuration items. Keep your cluster and node names unique if you need to keep separate instances on the same network from seeing each other.
Since we're only using ElasticSearch locally, we won't be configuring https.
nano /etc/elasticsearch/elasticsearch.yml
cluster.name: SITENAME-dotcms
node.name: SITENAME-dotcms-node1
network.host: 127.0.0.1
xpack.security.enabled: false
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false
Memory Configuration. Adjust as needed
nano /etc/elasticsearch/jvm.options
-Xms2g
-Xmx2g
Setup startup timeout. Add this to give ES extra time to start, and automatic restart.
systemctl edit elasticsearch.service
Add
[Unit]
StartLimitIntervalSec=240
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=10s
TimeoutSec=300
Restart ElasticSearch
systemctl restart elasticsearch
nano /usr/lib/systemd/system/dotcms.service
[Unit]
Description=dotCMS Service
After=network.target [NAME_OF_ES_SERVICE] [NAME_OF_DB_SERVICE]
Requires=[NAME_OF_ES_SERVICE] [NAME_OF_DB_SERVICE]
[Service]
Type=forking
Restart=always
RestartSec=10s
TimeoutSec=120
Environment="CATALINA_PID=/var/run/dotcms/dotcms.pid"
Environment="DOTCMS_HOME=/opt/dotcms"
Environment="ES_TLS_ENABLED=false"
Environment="ES_PROTOCOL=http"
PermissionsStartOnly=True
RuntimeDirectory=dotcms
RuntimeDirectoryMode=755
WorkingDirectory=/opt/dotcms
PIDFile=/var/run/dotcms/dotcms.pid
User=dotcms
Group=dotcms
LimitNOFILE=10000
KillMode=none
ExecStart=/opt/dotcms/bin/startup.sh
ExecStop=/opt/dotcms/bin/shutdown.sh
[Install]
WantedBy=multi-user.target
systemctl enable dotcms
systemctl start dotcms
More Information: systemd
tail -fn200 /opt/dotcms/dotserver/tomcat-8.5.32/logs/catalina.out
tail -fn200 /opt/dotcms/dotserver/tomcat-8.5.32/webapps/ROOT/dotsecure/logs/dotcms.log
nano /etc/httpd/conf.d/dotcms.conf
#################################
## Proxy to dotCMS:8080 (HTTP) ##
#################################
<IfModule mod_proxy_http.c>
<VirtualHost *:80>
ServerName example.com
ServerAdmin support@ethode.com
ErrorLog /var/log/httpd/error.log
CustomLog /var/log/httpd/access.log combined
ProxyRequests Off
ProxyPreserveHost On
ProxyVia On
ProxyPass / http://localhost:8080/ retry=0 acquire=3000 timeout=1200 Keepalive=On
ProxyPassReverse / http://localhost:8080/ retry=0
### Alternate for AJP
#ProxyPass / ajp://localhost:8009/ retry=0 acquire=3000 timeout=1200 Keepalive=On
#ProxyPassReverse / ajp://localhost:8009/ retry=0
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
## Redirect to https
<IfModule mod_ssl.c>
RewriteEngine on
RewriteCond %{SERVER_NAME} =example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</IfModule>
</VirtualHost>
</IfModule>
######################################
## Proxy to dotCMS SSL (HTTP/HTTPS) ##
######################################
<VirtualHost *:80>
ServerName DOTCMS_HOST
ServerAdmin admin@localhost
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =DOTCMS_HOST
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName DOTCMS_HOST
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
ProxyRequests Off
ProxyPreserveHost On
ProxyVia On
ProxyPass / http://localhost:8080/ retry=0 acquire=3000 timeout=1200 Keepalive=On
ProxyPassReverse / http://localhost:8080/ retry=0
### Alternate for AJP
# ProxyPass / ajp://localhost:8009/ retry=0 acquire=3000 timeout=1200 Keepalive=On
# ProxyPassReverse / ajp://localhost:8009/ retry=0
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/HOST_DIR/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/HOST_DIR/privkey.pem
</VirtualHost>
</IfModule>
setsebool -P httpd_can_network_connect 1
certbot certonly --webroot -w /opt/dotcms/dotserver/tomcat-8.5.32/webapps/ROOT -d domain.com
or
certbot --apache
More information: Certbot
crontab -e
* * */5 * * /root/certbot-auto renew > /var/log/letsencrypt-autorenew.log
nano /etc/monitrc
set eventqueue
set mmonit http://username:password@mmonithost.tld:8081/collector
Go to config directory: cd /etc/monit.d
wget https://raw.githubusercontent.com/x0rsw1tch/monit-presets/master/dotcms.conf
wget https://raw.githubusercontent.com/x0rsw1tch/monit-presets/master/httpd.conf
More Information: Monit Presets
systemctl enable iptables # Use iptables. Disable firewalld first!
iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Allow communication on incoming port
firewall-cmd --zone=public --permanent --add-port=80/tcp # Using firewalld, port 80
firewall-cmd --zone=public --permanent --add-port=443/tcp # Using firewalld, port 443
service iptables save # Save iptables config
systemctl daemon-reload # Reload systemd unit files (When making changes)
setenforce Permissive # Put SELinux in Permissive mode (Throw warnings instead of denial)
setsebool -P httpd_can_network_connect 1 # Allow Apache to connect to local host (reverse proxy)
setsebool -P httpd_can_network_connect_db 1 # Allow Apache to connect to DB over TCP
setsebool -P httpd_use_cifs 1 # Allow Apache to host in samba context
setsebool -P ssh_chroot_rw_homedirs 1 # Allow chrooted ssh to read write to home
semanage boolean -l # List Booleans
getsebool -a # List Booleans with explanation
semanage fcontext -a -t context_name /path # Change security context, accepts wildcards
restorecon -v /path # Commits change from fcontext, Use -r for recursive
ls -aZ # View dir with context column
semanage login -l # Show user contexts
ps -eZ # Show processes with contexts
tail -f -n100 /var/log/audit/audit.log # SELinux Log
aureport -a # Show SELinux audit summary
seinfo -r # Show SELinux roles
NOTE: As of version 5.3.0 (not released at time of writing), dotCMS will ship with an empty site, so this won't be necessary after it's release
Only do this after dotCMS is deployed and running. No need to restart dotCMS
- As Root:
su postgres
- Download SQL file
empty_starter_workflows_fix.sql
psql -d DATABASENAME -f /path/to/empty_starter_workflows_fix.sql