Setting up Jenkins on a Hatless Fedora Server

Setting up Jenkins on a Hatless Fedora Server

After setting up jenkins in windows I wanted to set up a jenkins on my headless fedora server, I imagined it would still be easier than on a GUI windows and would allow me to trigger builds from Unity Cloud. The current setup is I have a server with my own domain (this one) running my site, and worpress and anything else I want to run really.

This time instead of doing what I am doing with wordpress and accessing it the wrong way, I would like to access it like jenkins.ryanjames.io or something similar.

Installing Jenkins

I have sshd into my server and already it looks easier than windows, simple steps are set out by the jenkins docs specifically for installing with dnf https://www.jenkins.io/doc/book/installing/linux/#fedora

sudo wget -O /etc/yum.repos.d/jenkins.repo \
    https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo dnf upgrade
# Add required dependencies for the jenkins package
sudo dnf install fontconfig java-21-openjdk
sudo dnf install jenkins
sudo systemctl daemon-reload

All worked well. First hiccup was trying to run it, I initially. This is also documented and I followed the steps

sudo systemctl enable jenkins
sudo systemctl start jenkins

This failed for me with the following error

[root@ryanjames-fedora ~]# sudo systemctl start jenkins
^[[1;5DJob for jenkins.service failed because the control process exited with error code.
See "systemctl status jenkins.service" and "journalctl -xeu jenkins.service" for details.

And running the suggested journalctl command I saw it failed to start jetty

Aug 07 17:56:21 ryanjames-fedora jenkins[828567]: Caused: java.io.IOException: Failed to start Jetty

It didn’t give much info but I guessed it was port related as I was using 8080 already, so I ran the below to find where jenkins config was hiding as I couldn’t find it.

[root@ryanjames-fedora systemd]# systemctl cat jenkins

This told me it was at /usr/lib/systemd//system/jenkins.service and I was able to update the port here. After starting jenkins again we can see it is running

[root@ryanjames-fedora systemd]# systemctl daemon-reload
[root@ryanjames-fedora systemd]# systemctl restart jenkins
[root@ryanjames-fedora systemd]# systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
     Loaded: loaded (/usr/lib/systemd/system/jenkins.service; enabled; preset: disabled)
    Drop-In: /usr/lib/systemd/system/service.d
             └─10-timeout-abort.conf
     Active: active (running) since Thu 2025-08-07 18:08:45 UTC; 6s ago

At this point I can access jenkins at http://ryanjames.io:9020. It will allow us to go through the wizard, set up the admin and carry on as usual from this point.

There are still a couple of issues here:

  • We are accessing via http and this is insecure, we want to access via https to ensure requests are encrypted.
  • It is annoying needing to access it via the custom port 9020, and also needing to make this public, it should be blocked by firewalld.

Let’s discuss how to do these two things in the next section

Securing Jenkins

To allow us to access jenkins via https on port 443 we will be using Traefik. The setup in my case will be a little more complex than a typical case as I am running traefik inside docker, with other docker containers being handled by traefik in a docker network. Jenkins on the other hand is running on the host.

We begin by getting the docker0 gateway ip

ip -4 addr show docker0 | awk '/inet /{print $2}' | cut -d/ -f1

We need this ip for the next step. I am going to create a file that can be loaded by traefik to give it information about where to find jenkins on the host network

http:
  services:
    jenkins-svc:
      loadBalancer:
        servers:
          - url: "http://<docker0 ip here>:9020"
  routers:
    jenkins:
      rule: "Host(`jenkins.ryanjames.io`)"
      entryPoints: [ "websecure" ]
      service: "jenkins-svc"
      tls:
        certResolver: "myresolver"

I save this in a file at /opt/traefik/dynamic/jenkins.yml. Notice that the entrypoint is now websecure so that it will be watching for traffic that is coming in on port 443 with the host jenkins.ryanjames.io

When we give it the loadbalancer server url for docker0 we are giving it the ip of the bridge docker creates, this is the bridge to the host machine from the docker container and this is the ip the host will be reachable on. Essentially traefik is being told that you can reach the host on this ip, and 9020 is the port jenkins is running on in the host machine.

We then configure traefik to load this file in the docker config

command:
(1)  - --providers.file.directory=/etc/traefik/dynamic
(2)  - --providers.file.watch=true
volumes:
(3)  - /opt/traefik/dynamic:/etc/traefik/dynamic:ro

(1) The first command line tells traefik where to look for dynamic config files (2) Tells it to watch and update if the file changes.

(3) This simply binds the host folder at /opt/traefik/dynamic to the container folder at /etc/traefik/dynamic as a read-only volume.

After this traefik is set up to forward traffic coming in on jenkins.ryanjames.io

Creating a new Address (A) record

The final step is to create an A record for our domain. In my case the domain is ryanjames.io but we want to add an A record so that we can access our site via jenkins.ryanjames.io.

It is important to note that both ryanjames.io and jenkins.ryanjames.io are both making https request on port 443, so without traefik first we wouldn’t be using https, second we wouldn’t be routing to different ports like jenkins, or my go website.

As digital ocean is handling my domain records I will add two records

  • jenkins
  • www.jenkins

Not sure if other providers do the same but digital ocean adds the suffix ryanjames.io automatically.

Done!

After all this set up I can now run all my services with https in a single server, mixing both docker containers and host services. Currently we have this setup

  • traefik running in a container
  • go app running in a container
  • wordpress (and sql database) running in a container
  • jenkins running on the host

And with a combination of DNS A records and traefik we are able to access all the services cleanly and (maybe somewhat securely)

CAVEAT: I am not a security expert and this is mostly for learning in my case, I very much doubt everything I have done is of the highest security standards and am confident there will be gaps in my knowledge around this. It is important to note that if my site got attacked it would not be a big deal in my case.

Here are some things I think are secure about it though

  • We are using https
  • Jenkins is password protected and (soon to be) with added 2FA
  • WordPress is using 2FA
  • Firewalld is blocking all traffic except ports 80, 443 and 22

Overall it was definitely an interesting experience and I hope to add more services, and also follow up with some interesting stuff with jenkins such as triggering unity pipelines from cloud webhook triggers. Stay tuned!


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *