Request Tracker 6.0.4 on AWS User Guide
Overview
This guide covers launching and using the Request Tracker 6.0.4 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg help desk, IT service and project management listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Request Tracker (RT) is Best Practical's open source ticketing system, used for IT and service desks, security incident response, customer support and any work that arrives as requests. Each request is a ticket in a queue, with its history of correspondence and comments, owners, watchers, priorities, due dates, custom fields, links between tickets, saved searches, dashboards and scrips (rules that act when a ticket changes).
What the cloudimg image adds:
- No stock login. A fresh upstream RT has the user
rootwith the passwordpassword. On this image that password never exists: first boot creates RT's database with arootpassword unique to your instance, and only that password signs in. - Nothing baked into the image. The image ships no PostgreSQL cluster and no RT secrets, so every instance creates its own database, its own
$SecretKey(which signs RT's forms and session tokens) and its own TLS certificate on first boot. - No database password at all. RT runs under FastCGI as its own system user,
rt, and PostgreSQL 16 accepts RT's database role only over the local socket from processes running asrt(or root), by peer authentication. PostgreSQL has no network listener. - HTTPS from the first boot. nginx serves RT on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, a request for a host name the instance does not serve gets no answer, and the session cookie is
SecureandHttpOnly. - The address and name come from the connection and the configuration. RT is handed the host name you configure (never the
Hostheader a client sends), and it sees and logs the real client address; nginx removes every forwarding header a client could use to pretend to be someone else. - Abuse limits and cross site protection. The sign in form, the REST 1.0 and REST 2.0 interfaces and any request that carries an
Authorizationheader share a limit of 10 a minute per client address. Every other request that sends data (a form post, or anything with a body) is limited to 30 a minute per address, with a burst of 20, which is ample for a person working tickets: RT accepts a user name and password on any of them, so this bounds password guessing through every page rather than only the sign in form. A user name or password in a URL's query string, and a GET request with a body, are refused outright. The limits use the address the connection comes from, never a header, referrer or cookie a client sends. RT's referrer checks are on, so a link or form on another site cannot act in RT on your users' behalf. - Read only code and configuration. RT is built into
/opt/rt6, owned by root. Thertuser that runs it cannot change RT's code or its settings. - Metadata access blocked, and an outbound firewall for RT. nginx, RT, PostgreSQL and first boot are blocked from the instance metadata service, so an address an RT user enters gets nothing back from it, and the AMI requires IMDSv2. RT's own processes may connect only to public IPv4 addresses: never to the metadata service, to private or reserved ranges, to your VPC's own addresses, or to IPv6. You can let RT reach specific internal addresses, such as a mail relay inside your VPC (Step 9).
- Outgoing mail kept on the instance until you choose a relay. No mail server ships. RT writes the mail it would send (autoreplies and notifications) to files on the instance until you point it at your relay (Step 7).
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Request Tracker 6.0.4 (Best Practical's official release, checksum verified, built from source), the Perl modules it needs from Ubuntu 24.04 and, where Ubuntu's are too old or missing, from checksum pinned CPAN releases, nginx, PostgreSQL 16, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. No Ubuntu request-tracker package is used.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH) from your admin network, and TCP 443 (RT) and TCP 80 (redirects to HTTPS) from the networks your users work from. Your tickets are in RT, so keep 80 and 443 as narrow as you can.
- For e-mail, an SMTP relay such as Amazon SES for the mail RT sends.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Request Tracker 6.0.4 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for many users or a large ticket history), your key pair and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=request-tracker}]'
First boot creates the database and takes about two minutes after the instance is running.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Request Tracker 6.0.4 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot creates the PostgreSQL cluster, RT's database, the secret key, the root password and the certificate; nginx and RT start only after it has finished:
systemctl is-active nginx rt-fcgi postgresql@16-main rt-egress-guard rt-firstboot
sudo test -f /var/lib/cloudimg/rt-firstboot.done && echo FIRSTBOOT_DONE
All five units are active and the first boot marker is present (after a later reboot rt-firstboot shows inactive, because it runs only once):
active
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on network ports. PostgreSQL has no network listener at all and RT's FastCGI workers use a local socket:
sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\.0\.0\.5[34]/ {split($6, p, "\""); print $4, p[2]}' | sort
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
Step 4: Get your URL and password
sudo cat /root/request-tracker-credentials.txt
The file is readable by root only:
# Request Tracker 6.0.4 - generated on first boot by rt-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.
RT_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
RT_ADMIN_USER=root
RT_ADMIN_PASSWORD=<RT_ADMIN_PASSWORD>
#
# Open RT_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to https)
# and sign in as root with the password above. Then create your own privileged user and queues (Admin menu).
# PostgreSQL 16: unix socket only (no TCP listener, no database password; RT connects as the OS user rt).
# Settings: /opt/rt6/etc/RT_SiteConfig.d/ (add 60-local.pm, root:rt 0640; then: sudo systemctl restart rt-fcgi)
# Outgoing mail is written to /opt/rt6/var/*.mbox until you configure a relay (see the user guide).
# Change the root password: sudo rt-set-root-password
# Change the name it serves as: sudo rt-set-hostname <your-dns-name>
# User guide: https://www.cloudimg.co.uk/guides/request-tracker-6-0-4-on-ubuntu-24-04-aws/
The URL uses the instance's public DNS name from the EC2 instance metadata, and the certificate also names the public and private IP addresses. nginx answers only to the names in the certificate. The name is fixed at first boot, so if you stop and start the instance without an Elastic IP and its public address changes, run sudo rt-set-hostname with the new name (Step 8), or better, give the instance an Elastic IP or your own DNS name.
To check the sign in end to end from the instance itself (HTTPS redirect, an unknown host name refused, the root password through the real sign in form, a wrong password refused, and the stock root/password refused on both the sign in form and REST 1.0):
sudo rt-cred-roundtrip
OK https 301 + unknown Host refused + root sign-in + wrong password refused + stock root/password refused (form and REST 1.0)
Step 5: Sign in
Open the RT_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own, Step 8). Sign in with the user name root and the password from Step 4:

RT opens on your Homepage, with the highest priority tickets you own, the newest unowned tickets, your bookmarked tickets, a Quick ticket creation box, your reminders, the queue list and the dashboards:

Do not work as root. root is RT's superuser. Create a user for yourself (Step 6), give it the rights you need, and keep root for recovery. If you lose the root password, set a new one from the instance. Without an option it generates a random password and writes it to the credentials file; with --stdin it reads the password you type (24 to 72 letters and digits). Either way it signs every RT session out:
sudo rt-set-root-password
sudo rt-set-root-password --stdin
Step 6: Tickets, queues and users
Create a ticket. Choose Create new ticket at the top of any page, pick a queue, and enter the requestor, subject and message. RT ships one queue, General:

The ticket opens with its history. Reply writes to the requestor, Comment adds a note only staff see, and the Actions menu takes, resolves, rejects or links the ticket.
Queues. Create a queue for each team or kind of work under Admin, Queues, Create, then grant its rights under the queue's Group Rights and User Rights. A queue's Reply Address and Comment Address are the e-mail addresses its mail comes from.
The REST API. RT's REST 2.0 API is at /REST/2.0/. Create an authentication token for the user an integration acts as (the user menu with your initials at the top of the page, Settings, Auth Tokens) and send it as Authorization: token <token>. Requests to the API share the 10 a minute per address limit with the sign in form, which suits scripts and light integrations (the limits are in /etc/nginx/conf.d/cloudimg-rt.conf); put credentials in a header or a POST body, never in the URL, because a query string that names user or pass is refused.
Users. Add each person under Admin, Users, Create. Tick Let this user be granted rights (Privileged) for staff; unprivileged users are requestors who can use RT's self service pages. Set each user's password on their user page, and put staff in groups (Admin, Groups) so rights are granted to the group, not to each person:

Step 7: Mail
Outgoing mail stays on the instance until you configure it. No mail server ships, and RT is set to write each message it would send to a file under /opt/rt6/var/, named after the RT worker's start time:
sudo find /opt/rt6/var -maxdepth 1 -name '*.mbox' | wc -l
A new instance has sent nothing yet:
0
The count is above 0 once RT has sent an autoreply or notification, for example after you create a ticket in Step 6.
Send through a relay. Install a minimal sendmail replacement such as msmtp-mta, configure it for your relay in /etc/msmtprc (for Amazon SES: email-smtp.<region>.amazonaws.com, port 587, tls on, and your SES SMTP credentials), make that file root:rt mode 0640 so only RT can read the credentials, and tell RT to use it in your own settings file:
sudo apt-get install msmtp-mta
sudo install -o root -g rt -m 0640 /dev/null /opt/rt6/etc/RT_SiteConfig.d/60-local.pm
sudo nano /opt/rt6/etc/RT_SiteConfig.d/60-local.pm
Set($MailCommand, 'sendmailpipe');
Set($SendmailPath, '/usr/sbin/sendmail');
Set($CorrespondAddress, 'support@<your-domain>');
Set($CommentAddress, 'support-comment@<your-domain>');
Set($OwnerEmail, 'rt-admin@<your-domain>');
1;
sudo systemctl restart rt-fcgi
RT may reach public addresses, so a relay on the internet such as SES works as it is. A relay at a private address (inside your VPC or on premises) must be added to RT's outbound allow-list first (Step 9).
Incoming mail. RT receives mail through its rt-mailgate program, which your mail server or a fetch tool runs for each message, with the queue and the action (correspond or comment) and the RT URL; use --ca-file /etc/nginx/ssl/rt.crt while RT has the self signed certificate. rt-mailgate posts to RT's REST 1.0 mail gateway, so each message counts against the 10 a minute per address limit of REST 1.0. For more than that, deliver to RT from a separate host or ask support how to raise the limit for your mail server's address.
Step 8: Your own DNS name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name RT is served as. This mints a new self signed certificate for the name, points nginx and RT's $WebDomain at it, updates the URL in /root/request-tracker-credentials.txt, and keeps the name across reboots:
sudo rt-set-hostname <your-domain>
RT's own name ($rtname and $Organization, which appear in ticket subjects and links) is set once at first boot and does not change with the host name.
To use a certificate from a public or internal certificate authority instead (for example one issued by Let's Encrypt for <your-domain>), copy it over the generated pair and reload nginx (running rt-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/rt.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/rt.key
sudo nginx -t && sudo systemctl reload nginx
Step 9: What RT can connect to
RT connects out to mail relays and to any address a user or administrator puts into RT (for example in a scrip or an external link check). RT's processes run as the rt user, and rt-egress-guard.service loads an nftables table for that user at every boot. It lets RT reach public IPv4 addresses and DNS through the local resolver, and refuses the metadata service, loopback, private, shared and reserved IPv4 ranges, the IPv4 blocks of the instance's own VPC (read at every boot) and all IPv6:
sudo nft list table inet cloudimg_rt_egress | grep -oE 'comment "cloudimg-[a-z0-9]+"' | uniq
comment "cloudimg-loopback"
comment "cloudimg-metadata"
comment "cloudimg-metadata6"
comment "cloudimg-multicast"
comment "cloudimg-allow"
comment "cloudimg-allow6"
comment "cloudimg-private"
comment "cloudimg-vpc"
comment "cloudimg-ipv6"
To let RT reach an internal address, such as a mail relay inside your VPC, list it in /etc/cloudimg/rt-egress-allow (one private IPv4 or IPv6 CIDR per line; metadata, link-local, loopback and multicast ranges cannot be listed) and reload the table:
echo '10.0.5.25/32' | sudo tee -a /etc/cloudimg/rt-egress-allow
sudo systemctl reload rt-egress-guard
The table limits only the rt user. If you want to limit what the whole instance connects to, use its security group or a network ACL as well.
Step 10: Security checks
nginx serves only RT. Plain HTTP redirects to HTTPS, a request for a name the instance does not serve gets no answer at all, and RT is handed the configured name even when a client sends another one:
H=$(sudo grep '^RT_URL=' /root/request-tracker-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in / /NoAuth/Login.html /REST/1.0/ /.git/config; do
curl -sk -o /dev/null -w "%{http_code} $p\n" --resolve "$H:443:127.0.0.1" "https://$H$p"
done
curl -s -o /dev/null -w 'http:// -> %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'unknown host name -> %{http_code}\n' --resolve "other.example:443:127.0.0.1" "https://other.example/" || true
200 /
200 /NoAuth/Login.html
200 /REST/1.0/
404 /.git/config
http:// -> 301
unknown host name -> 000
The stock RT login is refused:
H=$(sudo grep '^RT_URL=' /root/request-tracker-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
curl -sk --resolve "$H:443:127.0.0.1" -d 'user=root' -d 'pass=password' "https://$H/REST/1.0/" | head -1
RT/6.0.4 401 Credentials required
RT's database role has no password; PostgreSQL accepts it only over the local socket by peer authentication, and PostgreSQL does not listen on the network:
sudo -u postgres psql -At -c "SELECT rolname || ' password set: ' || CASE WHEN rolpassword IS NULL THEN 'no' ELSE 'yes' END FROM pg_authid WHERE rolname = 'rt_user'" -c 'SHOW listen_addresses' -c 'SHOW ssl'
rt_user password set: no
off
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^RT_URL=' /root/request-tracker-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
None of the services can reach the instance metadata service (systemd prints the addresses in any order, so they are sorted here):
for u in nginx rt-fcgi postgresql@16-main rt-egress-guard rt-firstboot; do
echo "$u: $(systemctl show -p IPAddressDeny --value $u | tr ' ' '\n' | sort | paste -sd ' ')"
done
nginx: 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
rt-fcgi: 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
postgresql@16-main: 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
rt-egress-guard: 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
rt-firstboot: 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
Step 11: Backup, storage and maintenance
Everything you enter is in the PostgreSQL database rt6 (/var/lib/postgresql/16/main), including attachments. RT's settings and this instance's secret key are under /opt/rt6/etc. Both are on the root volume, so the simplest backup is an EBS snapshot of it. For a consistent copy while RT is running (root signs in to the database as RT's role over the socket):
umask 077
sudo pg_dump -U rt_user -Fc rt6 > <backup-dir>/rt6-$(date +%F).dump
sudo tar -C / -czf <backup-dir>/rt6-config-$(date +%F).tar.gz opt/rt6/etc
The dump holds every password hash and ticket, and the settings archive holds the secret key, so store them as carefully as the instance.
To give the database more room, enlarge the root volume in the EC2 console (Volumes, Modify); Ubuntu grows the file system on the next boot. RT removes sessions idle for more than 30 days every day (rt-clean-sessions.timer). Contact support if you would like help moving the database to its own EBS volume.
New RT releases arrive as new versions of this listing, and support can help you move your data to one. Operating system security updates install automatically; run sudo apt-get update && sudo apt-get upgrade to apply everything else. nginx, RT and PostgreSQL restart cleanly and RT keeps all of its data.
| Component | Path |
|---|---|
| Request Tracker | /opt/rt6 (owned by root; RT runs as the rt user) |
| Settings | /opt/rt6/etc/RT_SiteConfig.pm and /opt/rt6/etc/RT_SiteConfig.d/ (yours: 60-local.pm) |
| Secret key | /opt/rt6/etc/RT_SiteConfig.d/90-cloudimg-secrets.pm (root:rt, mode 0640) |
| Database | PostgreSQL 16 database rt6 (/var/lib/postgresql/16/main) |
| Outgoing mail files | /opt/rt6/var/*.mbox (until you configure a relay) |
| nginx site | /etc/nginx/sites-available/cloudimg-rt |
| Certificate and key | /etc/nginx/ssl/rt.crt, /etc/nginx/ssl/rt.key |
| Services | rt-fcgi.service, rt-egress-guard.service, rt-clean-sessions.timer |
| Outbound allow-list | /etc/cloudimg/rt-egress-allow |
| Helpers | /usr/local/sbin/rt-set-root-password, rt-set-hostname, rt-cred-roundtrip |
| Logs | journalctl -u rt-fcgi |
| Credentials | /root/request-tracker-credentials.txt (mode 0600) |
| Source | /usr/share/doc/cloudimg-request-tracker/SOURCE.txt |
Step 12: Security recommendations
- Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your users work from.
- Use your own DNS name and a certificate your users' browsers trust (Step 8).
- Create your own privileged user, give rights to groups rather than people, and keep
rootfor recovery with a password only you hold (Step 5). - Keep RT's outbound allow-list empty unless RT has to reach an internal address (Step 9).
- Keep the operating system patched, and snapshot the root volume regularly.
Support and licensing
Request Tracker is open source under the GNU General Public License version 2, by Best Practical Solutions and its contributors; there is no per user licence fee. The image ships RT exactly as released, built from source into /opt/rt6, which holds its complete source; /usr/share/doc/cloudimg-request-tracker/SOURCE.txt names the release, its download address and checksum, and the CPAN modules built for it. The AMI price covers cloudimg's hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. Request Tracker and RT are trademarks of Best Practical Solutions, LLC.