asciinema server
Overview
asciinema server is the self-hosted server for the asciinema terminal recorder. Record a session with the asciinema CLI, upload it to your own server, and share a link that replays it in a browser-based terminal player. Recordings are stored as timed text rather than video, so playback loads instantly, stays small, and keeps its text selectable, copyable and searchable by its actual terminal content.
The server adds a browsable library with full-text search, per-recording visibility (private, unlisted or public), secret sharing links, an embeddable player, downloadable .cast and plain text transcripts, live terminal streaming, and an administration panel.
This SecureAnyCloud image runs asciinema server 20260626 from the official container image, pinned to an immutable digest. PostgreSQL 16 runs on the same VM with its data directory on a dedicated Azure data disk, and nginx fronts the stack on port 80. Every VM generates its own session signing key, database password, administrator account and CLI upload token on first boot, and public sign-up ships switched off.
What you get:
- asciinema server
20260626(containerghcr.io/asciinema/asciinema-server, Apache-2.0), pinned to an immutablesha256digest so the shipped version never drifts - PostgreSQL 16, with its cluster data directory on a dedicated 32 GiB Azure data disk mounted at
/var/lib/asciinema - Recordings (
/var/lib/asciinema/uploads) and the derived render cache (/var/lib/asciinema/cache) on that same dedicated disk - nginx on
:80reverse proxying to the container, which publishes only to127.0.0.1:4000, with WebSocket upgrade headers and an unauthenticated/healthzendpoint for load balancer and uptime checks - A per-VM
SECRET_KEY_BASE, PostgreSQL password, administrator account and pre-authorized CLI upload token, all generated on first boot and recorded in a root-only file - Public sign-up disabled and anonymous uploads disabled by default, with the administrator account claimed during first boot
asciinema-login-url, a root-only helper that mints a fresh sign-in link on demand, so no mail server is neededdocker.service,postgresql.service,nginx.service,asciinema.serviceandasciinema-firstboot.serviceas systemd units- 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and is comfortable for a personal or team instance. Move up to Standard_B2ms or Standard_B4ms for a large library or many concurrent viewers.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp from wherever you will use asciinema. The server speaks plain HTTP on port 80. For anything reachable from the internet, terminate TLS in front of it with an Azure Application Gateway or Front Door, or with your own certificate on the VM's nginx, and use a real hostname.
PostgreSQL listens on the loopback interface and on the Docker bridge gateway so the application container can reach it. It is never bound to the VM's routable address, so never open 5432 in the NSG.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure portal, choose Create a resource, search for asciinema server on Ubuntu 24.04 LTS by SecureAnyCloud, and select Create.
Pick your subscription, resource group and region, and set the size to Standard_B2s. Choose SSH public key authentication with username azureuser, and allow inbound ports 22 and 80.
The image already carries the dedicated data disk definition, so the volume for recordings and the database is created and mounted for you.
Step 2 - Deploy from the Azure CLI
az vm image terms accept --urn sac1647283583153:asciinema:default:latest
az vm create \
--resource-group my-resource-group \
--name my-asciinema \
--image sac1647283583153:asciinema:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# then open the port asciinema serves on
az vm open-port --resource-group my-resource-group --name my-asciinema --port 80First boot takes about a minute. The VM generates its own secrets, runs the database migrations, claims the administrator account, and only then opens port 80.
Step 3 - Connect to your VM
Connect over SSH as azureuser:
ssh azureuser@<vm-public-ip>Step 4 - Confirm the services are running
Check that the services are active. All five units ship enabled. asciinema.service and postgresql.service are oneshot wrappers, so they report active (exited) once they have brought their payload up. That is the healthy state.
systemctl is-active docker.service postgresql.service nginx.service asciinema.serviceactive active active activeThe application runs as a single container, published to the loopback interface only:
sudo docker ps --filter 'name=^asciinema$' --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'NAMES STATUS PORTS asciinema Up 3 minutes 127.0.0.1:4000->4000/tcpConfirm that the running release is the pinned version rather than a development build:
sudo docker exec asciinema /opt/app/bin/asciinema rpc 'IO.puts(Application.get_env(:asciinema, :version))'20260626Query the unauthenticated health endpoint that nginx exposes for load balancers and uptime checks:
curl -s http://127.0.0.1/healthzokStep 5 - Sign in to the web interface
asciinema server authenticates with passwordless sign-in links, so there is no password to type. This image ships without a mail relay. Instead of emailing you a link, it provides a root-only helper that mints one on demand:
asciinema-login-urlhttp://13.68.186.216/session/new?t=SFMyNTY.g2gDaAJhAXcDbmlsbgYA...Open that URL in your browser. On the short confirmation page, click Log in to sign in as the instance administrator. Links are valid for 60 minutes (an upstream limit), so run the helper again whenever you need a fresh one. Pass a username or email to mint a link for a different account.
Your per-VM details, including the administrator username and the pre-authorized CLI upload token, are in a root-only file:
cat /root/asciinema-credentials.txt# asciinema server - generated on first boot by asciinema-firstboot.service
# These values are unique to this VM. Store them somewhere safe. asciinema.url=http://13.68.186.216/ asciinema.admin.username=sacadmin asciinema.admin.email=admin@sac.local asciinema.cli.token=3f9c1a2e-7b4d-4c8e-9a15-2d6e8f0b3c47 asciinema.db.password=... asciinema.login.url=...After you sign in, you land on the home page. It lists public recordings, so a new instance is empty here until public recordings are uploaded.
Uploads default to unlisted visibility. An unlisted recording is reachable by link but deliberately not shown in public listings, so an empty home page after an upload is the privacy default at work, not a fault. Change a recording's visibility from its own page.
Step 6 - Record and upload your first session
Install the asciinema CLI on the machine you want to record, such as your laptop or another server. It does not need to be this VM.
# macOS brew install asciinema
# Debian / Ubuntu
sudo apt install asciinemaThis image disables anonymous uploads, so the CLI must identify itself. Every VM generates a pre-authorized upload token bound to its administrator account. Write that token in as your CLI install ID:
mkdir -p ~/.config/asciinema echo <ASCIINEMA_CLI_TOKEN> > ~/.config/asciinema/install-idThen record a session and upload it to your own server:
asciinema rec --server-url http://<vm-public-ip>/ demo.cast
# ... run some commands, then press Ctrl-D to finish ... asciinema upload --server-url http://<vm-public-ip>/ demo.castThe server replies with a share link. Send that URL to anyone and they can replay your terminal in a browser:
View the recording at: http://13.68.186.216/a/OkIgeVCebJ8HbiBEYou can also call the HTTP API that the CLI uses directly from the VM. This command uploads a tiny two-frame recording with the per-VM token and prints the URL that the server returns:
cat > /tmp/guide-demo.cast <<'CAST' {"version": 2, "width": 80, "height": 24, "timestamp": 1700000000, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}} [0.1, "o", "$ echo hello from asciinema\r\n"] [0.4, "o", "hello from asciinema\r\n"] CAST curl -s -u "<ASCIINEMA_ADMIN_USERNAME>:<ASCIINEMA_CLI_TOKEN>" \
-F "asciicast=@/tmp/guide-demo.cast" http://127.0.0.1/api/asciicasts | jq -r '.url'http://13.68.186.216/a/OkIgeVCebJ8HbiBEThe link carries your VM's public address, not the address the request arrived on. The server builds share links from its configured URL_HOST, which first boot sets to this VM's public IP, so anyone you send a link to can open it. If you serve the instance on your own domain, update URL_HOST as described in "Serving asciinema on your own domain" below.
Open the returned link to replay the recording in the embedded player.
Your profile at /~<username> lists all of your recordings, whatever their visibility, with titles, durations and a visibility badge.
Step 7 - Confirm the security posture
Upstream asciinema server grants administrator rights to the first account that registers and leaves public sign-up open, so the first stranger to find a server could claim it. This image closes that gap before port 80 opens. Sign-up is disabled from the moment the server starts, and first boot claims the administrator account itself.
Confirm that exactly one administrator exists and that it is your per-VM account:
DBP=$(grep '^asciinema.db.password=' /root/asciinema-credentials.txt | cut -d= -f2-) PGPASSWORD="$DBP" psql -w -h 127.0.0.1 -U asciinema -d asciinema -tAc \
"SELECT count(*) FILTER (WHERE is_admin), string_agg(username, ',') FROM users;"1|sacadminConfirm that public sign-up and anonymous upload are both off, and that the upstream admin endpoint is explicitly rebound:
grep -E '^(SIGN_UP_DISABLED|UPLOAD_AUTH_REQUIRED|ADMIN_BIND_ALL|ADMIN_PANEL_ON_MAIN_ENDPOINT)=' /etc/asciinema/asciinema.envADMIN_BIND_ALL=0 ADMIN_PANEL_ON_MAIN_ENDPOINT=1 SIGN_UP_DISABLED=true UPLOAD_AUTH_REQUIRED=1Confirm that an unrecognized CLI token is refused rather than silently creating a throwaway account:
printf '%s\n' '{"version": 2, "width": 80, "height": 24, "timestamp": 1700000000, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}' > /tmp/anon-probe.cast printf '%s\n' '[0.1, "o", "anonymous probe\r\n"]' >> /tmp/anon-probe.cast curl -s -o /dev/null -w '%{http_code}\n' -u "nobody:$(uuidgen)" \
-F "asciicast=@/tmp/anon-probe.cast" http://127.0.0.1/api/asciicasts401Only nginx is reachable from the network. The application port, the administration port and PostgreSQL are all bound away from the VM's routable address. Verify the listeners:
ss -lntH | awk '{print $4}' | grep -E ':(80|4000|4002|5432)$' | sort0.0.0.0:80 127.0.0.1:4000 127.0.0.1:5432 172.17.0.1:5432 [::]:80172.17.0.1 is the Docker bridge gateway, a host-local interface reachable only from the application container and never from the network. Port 4002 does not appear at all. It is asciinema's administration endpoint, which performs no authentication of its own, so this image never publishes it. The administration panel is served at /admin behind an administrator session instead.
Signed in as the administrator, open /admin to reach the panel.
Step 8 - Recordings and the database on the dedicated disk
Everything durable lives on the dedicated data disk, so it survives a resize and is re-provisioned with every VM. Confirm the mount:
df -h /var/lib/asciinema | tail -1/dev/sdc 32G 59M 30G 1% /var/lib/asciinemasudo -u postgres psql -tAc 'SHOW data_directory' ls -1 /var/lib/asciinema/var/lib/asciinema/pgdata/16/main cache lost+found pgdata uploadsRecordings are stored under uploads/, the PostgreSQL cluster under pgdata/, and regenerable preview renders under cache/.
Adding more people to your instance
Public sign-up ships disabled, so nobody can register on your instance without you. To invite people, add them from the administration panel at /admin, or re-open public sign-up:
# This is the setting as shipped:
grep '^SIGN_UP_DISABLED=' /etc/asciinema/asciinema.env
# To open public sign up, flip it and restart asciinema:
#
sudo sed -i 's/^SIGN_UP_DISABLED=true$/SIGN_UP_DISABLED=false/' /etc/asciinema/asciinema.env
#
sudo systemctl restart asciinemaSIGN_UP_DISABLED=trueWithout a mail relay, a new user cannot receive a sign-in link by email. Mint one for them with sudo asciinema-login-url <their-username-or-email>. To enable email delivery, set SMTP_HOST (plus SMTP_PORT, SMTP_USERNAME and SMTP_PASSWORD as your relay requires) in /etc/asciinema/asciinema.env and restart.
To let unregistered CLIs upload anonymously, set UPLOAD_AUTH_REQUIRED=0 in the same file and restart. Pair this with UNCLAIMED_RECORDING_TTL="7,30" to hide unclaimed recordings after 7 days and delete them after 30.
Serving asciinema on your own domain
Share links, sign-in links and the live-updating pages are all built from URL_HOST, so it must match the address people actually use. First boot sets it to this VM's public IP. To move to your own hostname, update it and restart:
# As shipped, pointing at this VM's public address:
grep -E '^(URL_HOST|URL_SCHEME|URL_PORT)=' /etc/asciinema/asciinema.env
# For your own domain behind TLS, set:
# URL_HOST=asciinema.example.com
# URL_SCHEME=https
# URL_PORT=443
# then:
sudo systemctl restart asciinemaURL_HOST=13.68.186.216 URL_SCHEME=http URL_PORT=80Set this value correctly. The live-updating panels on the home, explore and stream pages open a WebSocket that upstream checks against URL_HOST. With a mismatched value, those panels cannot connect, even though every page still returns HTTP 200. Recording playback is server-rendered and is unaffected either way.
Terminate TLS on an Azure Application Gateway or Front Door, or add your own certificate to the VM's nginx at /etc/nginx/sites-available/sac-asciinema.
Maintenance
The image is patched at build time and ships with unattended security upgrades enabled. To upgrade asciinema server itself, edit the pinned image digest in /etc/asciinema/compose.yaml and restart. Database migrations run automatically on start.
grep -E '^\s+image:' /etc/asciinema/compose.yamlimage: ghcr.io/asciinema/asciinema-server@sha256:0474b2186be46b23f5e16dbd4319727dbb68f27a303705fbe632821bf524b313Back up the database and the uploads directory together. A recording's database row and its file are two halves of one object.
sudo -u postgres pg_dump asciinema > /var/lib/asciinema/asciinema-$(date +%F).sql
sudo tar czf /var/lib/asciinema/uploads-$(date +%F).tar.gz -C /var/lib/asciinema uploadsReview the first-boot log:
journalctl -u asciinema-firstboot.service --no-pager | tail -5Support
SecureAnyCloud images come with 24/7 support. Contact sales@secureanycloud.com with the VM name, the region, and the output of journalctl -u asciinema.service --no-pager | tail -50.
Upstream project documentation is at docs.asciinema.org. asciinema server is free and open source software under the Apache License 2.0. The SecureAnyCloud charge covers packaging, security patching, image maintenance and support.
Deploy on Azure
Launch asciinema server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com