Kitsu 1.1.1 on AWS User Guide
Overview
This guide covers launching and using the Kitsu 1.1.1 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.
Kitsu, by CGWire, is a collaborative production tracker for animation, VFX and games studios. A production holds its assets (characters, environments, props) and its sequences and shots, each with a task per department (storyboard, layout, animation, lighting, compositing and so on) and a status that moves from TODO through WIP to DONE. Artists upload previews for review, supervisors comment and approve them, and producers follow the schedule, the team's workload and playlists for review sessions. Kitsu 1.1.1 is the Zou 1.1.1 API server with the Kitsu 1.0.77 web interface.
What the cloudimg image adds:
- Nothing baked into the image. The image ships with no database, no administrator and no secret keys. First boot creates an empty PostgreSQL database with its own password, a Redis password, a new key for signing sign in tokens and a certificate for the instance, then creates the administrator
admin@example.comwith a password unique to your instance. - No default logins. The only account is the one first boot creates for you. The image's self test (Step 3) confirms that the password is accepted and that common passwords such as
admin,kitsu,passwordand123456are refused. - HTTPS from the first boot. nginx serves Kitsu on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, and a request for a host name the instance does not serve gets no answer.
- Only nginx is on the network. The Zou API (
127.0.0.1:5000), its event stream (127.0.0.1:5001), PostgreSQL and Redis listen on the loopback address only, Redis needs its per instance password, and the only open ports are 22, 80 and 443. - The configured host name and the real client address. nginx passes Zou the instance's configured name whatever
HostorX-Forwarded-*headers a client sends, and passes the address of the connection itself, so Kitsu's sign in log and its lock after repeated wrong passwords see the real client. Changes from another web site, and event stream connections from another web site, are refused. - Metadata access blocked. nginx, the Zou API, the event stream, the job worker, PostgreSQL, Redis, first boot and the
zou-managehelper are blocked from the instance metadata service, so an address a user enters can never reach it, and the AMI requires IMDSv2. - Supported components only. Zou runs from its own Python environment in
/opt/zou/zouenv. FFmpeg, which Zou uses to encode previews, is an upstream FFmpeg 8.1 static build checked against a pinned checksum, because Ubuntu's own package receives security fixes only through Ubuntu Pro. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Zou 1.1.1 (from PyPI, checksum verified) and the Kitsu 1.0.77 web interface (from the project's GitHub release, checksum verified), gunicorn, PostgreSQL 16, Redis 7.0, nginx, FFmpeg 8.1, Python 3.12, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
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 (Kitsu) and TCP 80 (redirects to HTTPS) from the networks your team works from. Your production plans and previews are in Kitsu, so keep 80 and 443 as narrow as you can.
- Room for previews. Uploaded previews are stored on the root volume, which is small on the AMI. Give the instance a larger root volume at launch (Step 1) for any real production.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Kitsu 1.1.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for a big team or heavy preview encoding), your key pair and a security group as above, and under Configure storage set the root volume to the size your previews need (for example 100 GiB).
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> \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3}' \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=kitsu}]'
First boot builds the database and takes about a minute after the instance is running. The web server starts only when it has finished.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Kitsu 1.1.1 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
PostgreSQL, Redis, the Zou API (zou), its event stream (zou-events), its job worker (zou-jobs) and nginx are running, and first boot has finished:
systemctl is-active postgresql@16-main redis-server zou zou-events zou-jobs nginx
sudo test -f /var/lib/cloudimg/kitsu-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network. The Zou API and event stream, PostgreSQL and Redis listen on the loopback address:
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
127.0.0.1:5000 python
127.0.0.1:5001 python
127.0.0.1:5432 postgres
127.0.0.1:6379 redis-server
[::1]:6379 redis-server
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
The image's self test checks the open ports, signs in with your password, confirms that common passwords and anonymous API calls are refused, and checks the HTTPS redirect. It takes about 30 seconds:
sudo kitsu-selftest.sh
[ports] off-box TCP exactly [22 80 443]; Zou API 127.0.0.1:5000; events 127.0.0.1:5001; PostgreSQL and Redis loopback
[ports] KITSU_PORTS_OK
[login] per-instance credential signs in (admin, token works, revoked on logout); weak and upstream-default passwords refused; anonymous API refused
[login] KITSU_LOGIN_OK
[http] port 80 redirects to https://<configured host>/
[api] Zou answers
KITSU_SELFTEST_OK
Step 4: Get your URL and password
sudo cat /root/kitsu-credentials.txt
The file is readable by root only:
# Kitsu 1.1.1 on Ubuntu 24.04 by cloudimg
# Generated on this instance's first boot. These credentials are unique to THIS instance.
kitsu.url=https://<public-dns-name>/
kitsu.admin.email=admin@example.com
kitsu.admin.password=<KITSU_ADMIN_PASSWORD>
# Sign in at the URL above with this e-mail address and password, then change the e-mail address and password in
# Kitsu (Profile). The certificate is a self-signed certificate made for this instance on its first boot: compare its
# SHA-256 fingerprint (sudo openssl x509 -in /etc/ssl/kitsu/kitsu.crt -noout -fingerprint -sha256) or replace it with a
# CA-issued certificate (see the user guide). Change the served name with sudo kitsu-set-hostname <name>.
Step 5: Sign in
Open the kitsu.url in your browser. The certificate is self signed, so your browser asks you to confirm it the first time; compare the fingerprint with the command in the credentials file, or install your own certificate (Step 8). Sign in with the e-mail address admin@example.com and the password from Step 4:

The administrator's address is a placeholder. Open your profile (the round badge at the top right), set your own e-mail address and choose a new password, then delete or update kitsu.admin.password in /root/kitsu-credentials.txt so it does not hold an old password. Add each member of your team as a person under People, with the narrowest role that fits (Artist, Supervisor, Production Manager, Studio Manager or Admin).
If you lose the administrator password, set a new one from the shell. The password is read from the terminal (16 to 128 letters and digits) and never appears on a command line; the command also creates the administrator if that address does not exist yet:
sudo kitsu-set-admin-password <your-email>
Step 6: Productions, assets and shots
After signing in you see your productions. Create one with Create a new production, choosing its type (short, feature, TV show), frame rate and resolution; Kitsu then sets up its asset types, task types and statuses:

The Assets page lists the production's characters, environments and props, with one column per department and the status of each task. Click a status to change it, assign the task or add a comment and a preview:

The Shots page does the same for the sequences and shots, with their frame counts and a status per department from storyboard to compositing:

The production menu at the top also leads to the breakdown, the playlists for review sessions, the schedule, the team and the production's settings. Previews you upload are encoded by Zou's job worker with FFmpeg and stored under /opt/zou/previews.
The REST API. The web interface works entirely through Zou's REST API at /api/, and so can your own scripts (for example with the gazu Python client). The API root answers without a sign in and reports the version:
curl -sk https://127.0.0.1/api/
{"api":"Zou","version":"1.1.1"}
Everything else needs a signed in user or a bot token (create bots under People); a token has the rights of the user or bot it belongs to.
Step 7: E-mail and other settings
Kitsu sends notifications and password reset links by e-mail. The image has no mail server, so point Zou at your SMTP relay. Zou's settings are in /etc/zou/zou.env (readable by root and the zou service account only). Change the MAIL_DEFAULT_SENDER line (it starts as no-reply@ the instance's name) to the address Kitsu should send from, and add your relay's settings at the end of the file:
sudo nano /etc/zou/zou.env
MAIL_SERVER=<smtp-host>
MAIL_PORT=587
MAIL_USE_TLS=True
MAIL_USERNAME=<smtp-user>
MAIL_PASSWORD=<smtp-password>
Then restart the Zou services:
sudo systemctl restart zou zou-events zou-jobs
The same file takes Zou's other settings, such as DEFAULT_TIMEZONE=<Area/City> (Zou's default is Europe/Paris). Settings you add are kept when kitsu-set-hostname (Step 8) rewrites the file. Leave the database, Redis and SECRET_KEY lines as they are: first boot generated them for this instance, and the services refuse to start with the placeholders the image ships.
Zou's own command line runs as the zou service account, with the same metadata block, through zou-manage:
sudo zou-manage version
sudo zou-manage clean-auth-tokens
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 Kitsu is served as. This mints a new self signed certificate for the name, points nginx and Zou (the links in its e-mails) at it, updates the URL in /root/kitsu-credentials.txt, and keeps the name across reboots:
sudo kitsu-set-hostname <your-domain>
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 kitsu-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/kitsu/kitsu.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/kitsu/kitsu.key
sudo nginx -t && sudo systemctl reload nginx
Step 9: Security checks
nginx serves only the application. Without a sign in, the API answers 401, hidden files answer 404, plain HTTP redirects to HTTPS, and a request for a name the instance does not serve gets no answer at all:
H=$(sudo grep '^kitsu.url=' /root/kitsu-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in / /productions /api/ /api/data/projects /api/data/persons /.git/config /.env; 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 /productions
200 /api/
401 /api/data/projects
401 /api/data/persons
404 /.git/config
404 /.env
http:// -> 301
unknown host name -> 000
PostgreSQL listens on the loopback address only, and Redis refuses a client without its password:
sudo -u postgres psql -X -tA -c "SHOW listen_addresses"
redis-cli -h 127.0.0.1 ping
localhost
NOAUTH Authentication required.
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^kitsu.url=' /root/kitsu-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, so an address a user enters can never reach it:
systemctl show nginx zou zou-events zou-jobs postgresql@16-main redis-server kitsu-firstboot -p Id -p IPAddressDeny
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=nginx.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=zou.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=zou-events.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=zou-jobs.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=postgresql@16-main.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=redis-server.service
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=kitsu-firstboot.service
Step 10: Backup, storage and maintenance
Your productions, people and comments are in the PostgreSQL database zoudb (/var/lib/postgresql/16/main); uploaded previews are under /opt/zou/previews, and Zou's settings and keys are in /etc/zou. All of it is on the root volume, so the simplest backup is an EBS snapshot of it. For a consistent copy while Kitsu is running:
sudo -u postgres pg_dump -Fc zoudb > <backup-dir>/kitsu-$(date +%F).dump
sudo tar -C / -czf <backup-dir>/kitsu-files-$(date +%F).tar.gz opt/zou/previews etc/zou
The dump and /etc/zou hold every password hash and key, so store them as carefully as the instance.
Previews grow quickly. To give them more room, enlarge the root volume in the EC2 console (Volumes, Modify); Ubuntu grows the file system on the next boot.
New Kitsu releases arrive as new versions of this listing; 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, PostgreSQL, Redis and the Zou services restart cleanly and keep all of their data.
| Component | Path |
|---|---|
| Zou API server | /opt/zou/zouenv (Python environment), /opt/zou/pip-freeze.txt (installed packages) |
| Kitsu web interface | /opt/kitsu/dist |
| Settings and keys | /etc/zou/zou.env, /etc/zou/gunicorn-api.py, /etc/zou/gunicorn-events.py |
| Database | PostgreSQL 16 database zoudb (/var/lib/postgresql/16/main) |
| Previews | /opt/zou/previews |
| Redis password | /etc/redis/cloudimg-auth.conf |
| nginx site | /etc/nginx/sites-available/kitsu |
| Certificate and key | /etc/ssl/kitsu/kitsu.crt, /etc/ssl/kitsu/kitsu.key |
| Services | zou, zou-events, zou-jobs, postgresql@16-main, redis-server, nginx |
| Helpers | /usr/local/sbin/kitsu-set-hostname, kitsu-set-admin-password, zou-manage, kitsu-selftest.sh |
| FFmpeg | /usr/local/bin/ffmpeg, /usr/local/bin/ffprobe (licence and source in /usr/local/share/doc/ffmpeg-gpl) |
| Credentials | /root/kitsu-credentials.txt (mode 0600) |
Step 11: Security recommendations
- Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your team works from.
- Use your own DNS name and a certificate your team's browsers trust (Step 8).
- Replace the placeholder administrator address and password (Step 5), and give each person their own account with the narrowest role that fits.
- Use bot tokens rather than passwords for scripts, one bot per script, and remove bots you no longer use.
- Install plugins only from sources you trust: a plugin runs inside Zou with full access to its data.
- Keep the operating system patched, and snapshot the root volume regularly.
Support and licensing
Kitsu and Zou are open source under the GNU Affero General Public License v3.0, by CGWire; the licence is in /opt/kitsu/LICENSE, and the source of the exact releases on the image is listed in /opt/kitsu/SOURCE.txt. FFmpeg is included as the upstream GPL build, with its licence and source location in /usr/local/share/doc/ffmpeg-gpl. There is no per user licence fee. 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.