This Dockerfile allows you to build images to deploy your own TeamCity instance. It has been tested on Fedora 23 and CentOS 7.
Please remember to back up your data directories often, especially before upgrading to a newer version.
- Install docker.
- Run the container. (Stop with CTRL-C.)
docker run -it --publish 8111:8111 agross/teamcity
- Open your browser and navigate to
http://localhost:8111
.
- Decide where to put TeamCity data and logs. Set domain name/server name and the public port.
TEAMCITY_DATA="/var/data/teamcity"
TEAMCITY_LOGS="/var/log/teamcity"
DOMAIN=example.com
PORT=8011
- Create directories to store data and logs outside of the container.
mkdir --parents "$TEAMCITY_DATA" \
"$TEAMCITY_LOGS"
- Set permissions.
The Dockerfile creates a teamcity
user and group. This user has a UID
and
GID
of 3000
. Make sure to add a user to your host system with this UID
and GID
and allow this user to read and write to $TEAMCITY_DATA
and
$TEAMCITY_LOGS
. The name of the host user and group in not important.
# Create teamcity group and user in docker host, e.g.:
groupadd --gid 3000 \
--system teamcity
useradd --uid 3000 \
--gid 3000 \
--system \
--shell /sbin/nologin \
--comment "JetBrains TeamCity" \
teamcity
# 3000 is the ID of the teamcity user and group created by the Dockerfile.
chown -R 3000:3000 "$TEAMCITY_DATA" "$TEAMCITY_LOGS"
- Create your container.
Note: The :z
option on the volume mounts makes sure the SELinux context of
the directories are set appropriately.
Use --env
to specify JVM and TeamCity server options, e.g. for
memory.
/etc/localtime
needs to be bind-mounted to use the same time zone as your
docker host.
docker create -it --env TEAMCITY_SERVER_MEM_OPTS='-Xms1g -Xmx3g' \
--publish $PORT:8111 \
--volume /etc/localtime:/etc/localtime:ro \
--volume "$TEAMCITY_DATA:/teamcity/.BuildServer:z" \
--volume "$TEAMCITY_LOGS:/teamcity/logs:z" \
--name teamcity \
agross/teamcity
- Create systemd unit, e.g.
/etc/systemd/system/teamcity.service
.
cat <<EOF > "/etc/systemd/system/teamcity.service"
[Unit]
Description=JetBrains TeamCity Server
Requires=docker.service
After=docker.service
[Service]
Restart=always
# When docker stop is executed, the docker-entrypoint.sh trap + wait combination
# will generate an exit status of 143 = 128 + 15 (SIGTERM).
# More information: http://veithen.github.io/2014/11/16/sigterm-propagation.html
SuccessExitStatus=143
PrivateTmp=true
ExecStart=/usr/bin/docker start --attach=true teamcity
ExecStop=/usr/bin/docker stop --time=60 teamcity
[Install]
WantedBy=multi-user.target
EOF
systemctl enable teamcity.service
systemctl start teamcity.service
- Setup logrotate, e.g.
/etc/logrotate.d/teamcity
.
cat <<EOF > "/etc/logrotate.d/teamcity"
$TEAMCITY_LOGS/*.log
{
rotate 7
daily
dateext
missingok
notifempty
sharedscripts
copytruncate
compress
}
EOF
- Add nginx configuration, e.g.
/etc/nginx/conf.d/teamcity.conf
.
cat <<EOF > "/etc/nginx/conf.d/teamcity.conf"
upstream teamcity {
server localhost:$PORT;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' '';
}
server {
listen 80;
listen [::]:80;
server_name $DOMAIN;
access_log /var/log/nginx/$DOMAIN.access.log;
error_log /var/log/nginx/$DOMAIN.error.log;
# Do not limit upload.
client_max_body_size 0;
proxy_read_timeout 1200;
proxy_connect_timeout 240;
# Required to avoid HTTP 411: see issue #1486 (https://gitteamcity.com/dotcloud/docker/issues/1486)
chunked_transfer_encoding on;
location / {
proxy_pass http://teamcity;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-Host \$http_host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
# Support WebSockets.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
EOF
nginx -s reload
Make sure SELinux policy allows nginx to access port $PORT
(the first part
of --publish $PORT:8080
of step 3).
EXPR="^http_port_t\s+tcp\s.*\b$PORT\b"
if ! semanage port --list | grep --quiet --perl-regexp "$EXPR"; then
if semanage port --add --type http_port_t --proto tcp $PORT; then
echo Added port $PORT as a valid port for nginx:
semanage port --list | grep --perl-regexp "$EXPR"
else
>&2 echo Could not add port $PORT as a valid port for nginx. \
Please add it yourself. More information: \
http://axilleas.me/en/blog/2013/selinux-policy-for-nginx-and-gitlab-unix-socket-in-fedora-19/
fi
else
echo Port $PORT is already a valid port for nginx:
semanage port --list | grep --perl-regexp "$EXPR"
fi
- Configure TeamCity.
Follow the steps of the installation
instructions for JetBrains TeamCity
using paths inside the docker container located under /teamcity/.BuildServer
.
- Update to a newer version.
docker pull agross/teamcity
systemctl stop teamcity.service
# Back up $TEAMCITY_DATA.
tar -zcvf "teamcity-data-$(date +%F-%H-%M-%S).tar.gz" "$TEAMCITY_DATA"
docker rm teamcity
# Repeat step 4 and create a new image.
docker create ...
systemctl start teamcity.service
- Trust self-signed SSL certificates.
If you need to connect to e.g. an LDAP server that uses a self-signed certificate or use the NuGet trigger with a NuGet feed served with a self-signed certificate you need to add those certificates to the JVM trust store inside the container.
With the docker container running, execute:
HOST=ldap.example.com:636
docker exec -it -u root teamcity_teamcity_1 bash -c "
set -o pipefail
echo -n |
openssl s_client -connect "$HOST" |
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > "/tmp/$HOST" &&
"\$JAVA_HOME/bin/keytool" -import \
-alias "$HOST" \
-file "/tmp/$HOST" \
-keystore "\$JAVA_HOME/jre/lib/security/cacerts" \
-noprompt \
-storepass changeit &&
rm /tmp/$HOST"
- Build the
Dockerfile
.
docker build --tag agross/teamcity:testing .
docker images
# Should contain:
# REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
# agross/teamcity testing 0dcb8bf6093f 49 seconds ago 405.4 MB
- Prepare directories for testing.
TEST_DIR="/tmp/teamcity-testing"
mkdir --parents "$TEST_DIR/data" \
"$TEST_DIR/logs"
chown -R 3000:3000 "$TEST_DIR"
- Run the container built in step 1.
Note: The :z
option on the volume mounts makes sure the SELinux context of
the directories are set appropriately.
docker run -it --rm \
--name teamcity-testing \
--publish 8111:8111 \
--volume "$TEST_DIR/data:/teamcity/.BuildServer:z" \
--volume "$TEST_DIR/logs:/teamcity/logs:z" \
agross/teamcity:testing
- Open a shell to your running container.
docker exec -it teamcity-testing bash
- Run bash instead of starting TeamCity.
Note: The :z
option on the volume mounts makes sure the SELinux context of
the directories are set appropriately.
docker run -it --rm \
--name teamcity-testing \
--publish 8111:8111 \
--volume "$TEST_DIR/data:/teamcity/.BuildServer:z" \
--volume "$TEST_DIR/logs:/teamcity/logs:z" \
agross/teamcity:testing \
bash