Rs
Media & Entertainment AWS

Ryot Self-Hosted Media Tracker on AWS User Guide

| Product: Ryot on AWS

Overview

This image runs Ryot, a self-hosted media tracker and aggregator that keeps everything you read, watch, play and listen to in one place. Ryot tracks movies, TV shows, books, video games, audiobooks, podcasts, manga and anime, lets you organise them into collections, rate and review them, and see your progress and history across every kind of media on infrastructure you own.

Ryot ships as a single official Docker image (a Rust backend that serves the compiled web frontend on port 8000) that points at a PostgreSQL database. In this cloudimg image the Ryot container is bound to 127.0.0.1:8000 on the loopback interface and fronted by nginx as a reverse proxy on port 80 (and 443 once you add TLS). PostgreSQL is co-hosted on the same instance and reachable only from the loopback interface and the Docker bridge - the database port is never exposed by the security group. The Ryot container image is version-frozen to a pinned image digest so the bytes never change under you.

The entire Ryot datastore - users, tracked media, collections, reviews and import history - lives on a dedicated EBS data volume mounted at /var/lib/postgresql, independently resizable and separate from the operating system disk. On the first boot of every deployed instance, a one-shot service rotates the PostgreSQL password to a value unique to that instance, recreates an empty database, generates a per-instance admin access token, binds the application to the instance's own address, and records the token in /root/ryot-credentials.txt with mode 0600. No shared or default secrets ship in the image. Because Ryot makes the first account you register the administrator, the very first thing you do after launch is open the web interface and create your own admin account.

Ryot dashboard

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound ports 80 and 443 from the networks that will reach the application
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line
  • A DNS domain you control, if you intend to serve Ryot on your own hostname with TLS (covered in Step 7). You can run and verify the application on its own AWS address without a domain first.

Step 1: Launch the Instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for Ryot. Select the cloudimg listing and choose Select, then Continue on the subscription summary.

Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and either create or select a security group that allows inbound port 22 from your management network and inbound ports 80 and 443 from the networks that will reach the application. Leave the root volume at the default size or larger; the image adds a separate 20 GiB data volume for the PostgreSQL datastore automatically.

Select Launch instance. First boot initialisation takes a minute or two after the instance state becomes Running and the status checks pass, while PostgreSQL is prepared and the Ryot container starts.

Step 2: Launch the Instance from the AWS CLI

The following block launches an instance from the cloudimg Ryot Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22, 80, and 443 as described above.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ryot-01}]'

The command prints a JSON document on success. Note the instance ID, then retrieve its public address once it is running with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.

Step 3: Connect and Retrieve the Admin Access Token

Connect over SSH with the key pair you selected and the public IP address from step 2. The SSH login user depends on the operating system of the AMI variant you launched:

AMI variant SSH login user
Ryot 10.4 on Ubuntu 24.04 ubuntu
ssh -i /path/to/your-key.pem ubuntu@<public-ip>

A per-instance admin access token is generated on first boot and written to a root-only file. Read it with:

sudo cat /root/ryot-credentials.txt

The file records ryot.url (initially the instance's own AWS address) and ryot.admin.access.token. The admin access token is Ryot's SERVER_ADMIN_ACCESS_TOKEN - a per-instance secret that authorizes server-administration operations against the GraphQL API. The database password and this token also live in /etc/ryot/ryot.env (mode 0600). Note that this token is not your login: Ryot accounts are created through the web interface, and the first account you register becomes the administrator (see Step 5).

Step 4: Verify the Application Is Healthy

The Ryot container listens on the loopback interface and nginx fronts it on port 80. Confirm the health endpoint answers and that the supporting services are running.

curl -s -o /dev/null -w 'ryot /health -> HTTP %{http_code}\n' http://localhost/health

A healthy instance returns HTTP 200. nginx also serves an unauthenticated static endpoint for load balancer and uptime checks:

curl -s -o /dev/null -w 'nginx /healthz -> HTTP %{http_code}\n' http://localhost/healthz

Confirm the Docker engine, PostgreSQL, nginx and the Ryot service are all active:

sudo systemctl is-active docker postgresql nginx ryot

You can see the version-frozen container and its loopback port binding with:

sudo docker ps --format '{{.Image}}  {{.Status}}  {{.Ports}}'

The Ryot GraphQL API is served under /backend/graphql. Ryot returns HTTP 200 for GraphQL requests and reports authorization outcomes in the response body, so an unauthenticated request to a protected query returns a NO_USER_ID error rather than an HTTP error code:

curl -s http://localhost/backend/graphql -H 'Content-Type: application/json' \
  -d '{"query":"query { userCollectionsList { cacheId } }"}'

Step 5: Register the First Account (Administrator)

Open http://<public-ip>/ in a browser. Ryot redirects to its authentication page. Choose Create a new account?, then pick a username and password and select Register. The first account registered on the instance is automatically made the administrator.

Registering and browsing media in Ryot

Sign in with the account you just created. From the dashboard you can search any media type - movies, TV shows, books, video games, audiobooks, podcasts, manga and anime - and add items to your library. Each media entry has a rich detail page with metadata, cast and crew, progress tracking, reviews and collection membership.

Ryot media detail page

Ryot creates a set of default collections for every account - Watchlist, In Progress, Completed, Monitoring, Reminders, Owned and Custom - and you can create your own from the Collections page.

Ryot collections

Closing open registration

Registration is open by default so you can create that first administrator account. Once your accounts exist, you can prevent new sign-ups by setting USERS_ALLOW_REGISTRATION=false in the environment file and restarting the service:

echo 'USERS_ALLOW_REGISTRATION=false' | sudo tee -a /etc/ryot/ryot.env
sudo systemctl restart ryot

Step 6: Where Your Data Lives

The complete Ryot datastore is held in the co-hosted PostgreSQL database, whose data directory sits on a dedicated EBS volume mounted at /var/lib/postgresql, separate from the operating system disk so you can resize, snapshot and back it up independently.

df -h /var/lib/postgresql

The Ryot application configuration (database connection, front-end URL and admin token) is in /etc/ryot/ryot.env, and the pinned container definition is in /etc/ryot/compose.yaml. The database is reachable only from the loopback interface and the Docker bridge; port 5432 is never opened to the network by the security group.

Step 7: Serve Ryot on Your Own Domain with TLS

The image ships domain-free and binds Ryot to the instance's own address so it is usable immediately. To serve it on your own name, point a DNS record for your-domain at the instance's public address, then set FRONTEND_URL in /etc/ryot/ryot.env to your URL and restart the service. Terminate TLS either on an AWS Application Load Balancer in front of the instance, or by adding your own certificate to nginx on the instance. The Ryot container itself continues to serve plain HTTP on the loopback interface; nginx (or the load balancer) handles HTTPS.

To change the front-end URL to your-domain, edit /etc/ryot/ryot.env, set FRONTEND_URL=https://your-domain/, and run sudo systemctl restart ryot.

Step 8: Backup and Maintenance

Because all state is in PostgreSQL on the dedicated /var/lib/postgresql volume, your backup strategy is a standard PostgreSQL backup plus, optionally, EBS snapshots of the data volume. Take a logical dump of the Ryot database with the postgres superuser (run on the instance):

sudo -u postgres pg_dump ryot > /var/tmp/ryot-backup.sql

Schedule regular EBS snapshots of the data volume through Amazon Data Lifecycle Manager for point-in-time recovery, and enable EBS encryption on the data volume for encryption at rest. To upgrade Ryot, update the pinned image digest in /etc/ryot/compose.yaml to a newer ghcr.io/ignisda/ryot release and run sudo systemctl restart ryot; the Rust backend runs any required database migrations automatically on start.

Support

This image is maintained by cloudimg. For deployment assistance, configuration help, TLS termination, database administration, upgrades or any other question about the image, contact cloudimg support at support@cloudimg.co.uk. Ryot is free and open source software distributed under the GNU General Public License v3.0; the complete corresponding source code is available at https://github.com/IgnisDa/ryot.