Deployment guide

Outline Server 1.12.3

Overview

Outline Server (also known by its container name, Shadowbox) is the server half of the open-source Outline access system. This SecureAnyCloud image runs Outline Server 1.12.3 on Ubuntu 24.04 on Amazon EC2, ready to use from first boot. It is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace: you choose it as the software version when you subscribe and launch. The other servers in the listing are described on the listing's guide hub.

You hand access keys to people. Each person pastes a key into the free Outline Client app (Windows, macOS, Linux, iOS, Android, ChromeOS), and their traffic travels to your server over Shadowsocks, an encrypted proxy protocol, and out to the internet from your instance. You manage the server and its keys from the Outline Manager desktop app, or from the command line on the instance.

How this image differs from the upstream installer:

  • Rebuilt on a patched runtime, with no source change. The upstream 1.12.3 container image runs Node.js 18.18.0, which is affected by CVE-2024-22019 (a denial of service against the public management API), on Alpine Linux 3.18, which is end of life. This image keeps the exact 1.12.3 application, with files byte-identical to upstream's, and rebuilds the container around it. The rebuild uses Node.js 18.20.8 on a fully patched Alpine 3.21 (the base Outline's own development branch has moved to), Prometheus 2.53.4 LTS, and outline-ss-server 1.7.3 recompiled with Go 1.27.3 and current golang.org/x/crypto. The build recipe is in /usr/share/doc/sac-outline/.
  • No Watchtower. Upstream's installer also runs Watchtower, which replaces the server image automatically and holds the Docker socket. This image does not include it, so the server software changes only when you decide (Step 12).
  • Nothing pre-made. The management API secret, the TLS key and the certificate are generated on your instance at first boot. The server starts with zero access keys.
  • The image ships its container. The rebuilt container is stored on the AMI as a checksummed archive and loaded at first boot. The instance needs no container registry access to start, although it does need outbound internet to serve clients.

What is on the image:

  • Outline Server 1.12.3 (outline-vpn-server.service), running in Docker CE with exactly three published ports: TCP 60443 (the management API Outline Manager talks to) and TCP and UDP 60444 (the Shadowsocks port your access keys use)
  • Docker CE and containerd from Docker's own signed repository, kept up to date by Ubuntu's unattended upgrades
  • sac-outline-guard.service: the firewall rule that keeps clients away from the instance metadata service
  • Helpers: outline-keys (create, list and revoke access keys; print the Outline Manager line), outline-selftest (an end-to-end test with a real Shadowsocks client), outline-imds-guard (show the metadata guard) and outline-regenerate (start over with a new server identity)

Default user: ubuntu. Architecture: x86_64. Root volume: 8 GiB gp3.

Security posture

  • Per-instance administrator secret. The management API has no password: it is protected by a random 128-bit path in its URL and by a self-signed certificate whose SHA-256 fingerprint Outline Manager pins. Both are generated on your instance at first boot. The line you paste into Outline Manager is written only to /root/outline-vpn-server-credentials.txt and /opt/outline/access.txt, both readable by root alone. It is never written to the system journal, the console or Docker's logs (the server's own start-up message, which includes the path, is redacted before it reaches the journal).
  • End-entity certificate. The management API's certificate can only vouch for this server (it is not a certificate authority). Do not install it in any trust store: compare its fingerprint instead (Step 5).
  • No access keys until you create them. First boot checks that the server has none.
  • Anonymous metrics off. Outline can share anonymous usage metrics with its developers. On this image that setting is off; turn it on from Outline Manager if you choose to.
  • Fail-closed start. The server cannot start before first boot has generated its secrets and loaded the metadata guard: its unit is gated on a ready marker and a preflight check. If first boot fails, the server stays stopped.
  • Metadata service protection. A connected client's traffic leaves your instance from the Outline container. Three separate walls keep it away from the instance metadata service (and from your instance's IAM role credentials): outline-ss-server refuses loopback, link-local and private destinations; a Docker firewall rule (DOCKER-USER) drops everything the container sends to 169.254.0.0/16, 168.63.129.16 and the IPv6 metadata addresses; and Docker, containerd and the Outline unit are denied the metadata service by systemd IPAddressDeny.
  • Container hardening. The container runs on Docker's private bridge network with only its three ports published (Prometheus and the internal metrics endpoints are not reachable from outside the container), with no-new-privileges, several Linux capabilities removed, no access to the Docker socket, and only its own state directory mounted. Docker listens on its local socket only.
  • Hardened SSH. Key-only login, no root login, no X11 forwarding.

Prerequisites

  • An AWS account subscribed to the listing, and an EC2 key pair in your Region.
  • A VPC subnet with internet access. Use a public subnet with a public IP, or a private subnet behind a NAT gateway or load balancer that forwards TCP 60443 and TCP/UDP 60444.
  • The free Outline Manager app on your computer and the Outline Client app on each device. Both are available from https://getoutline.org/.

Connecting to your instance

  • SSH user: ubuntu
  • Management API (Outline Manager): https://<instance-address>:60443/<secret path>, with the secret path taken from the credentials file
  • Access keys (Outline Client): TCP and UDP 60444
  • Credentials file: /root/outline-vpn-server-credentials.txt, readable by root only
1

Launch the instance

Option A: AWS Marketplace console

  • Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  • When the subscription is active, choose Launch your software.
  • Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Outline Server 1.12.3 on Ubuntu 24.04, then choose your Region.
  • Pick an instance type and your key pair. m5.large is recommended, and the software charge is $0.04 per vCPU per hour.
  • Review the security group. The listing's default group allows SSH (TCP 22), the management API (TCP 60443) and the access-key port (TCP and UDP 60444) from anywhere. Keep the access-key port open to wherever your users are. Restrict TCP 22 and TCP 60443 to your own IP address, because only you and your Outline Manager need them and the management API is your administrator interface.
  • Choose a subnet with internet access and a public IP. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.

Access keys contain the server's address. To keep them working when the instance stops and starts, attach an Elastic IP and point the server at it (Step 9).

Option B: AWS CLI

Run these commands on your own workstation. They create a security group that allows SSH and the management API from your address and the access-key port from anywhere, then launch the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and address with your own.

SG=$(aws ec2 create-security-group --group-name outline-server \
  --description "Outline Server" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 60443 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 60444 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 60444 --cidr 0.0.0.0/0
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --associate-public-ip-address \
  --metadata-options HttpTokens=required,HttpPutResponseHopLimit=1 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=outline-server}]'

HttpPutResponseHopLimit=1 is optional but recommended. It stops anything running inside a container on the instance from obtaining an IMDSv2 token at all, as a fourth wall behind the three described above. The image itself already requires IMDSv2.

2

Connect and read your server details

Connect over SSH as ubuntu with the key pair you chose.

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute of launch. It generates the management API secret and the TLS certificate, loads the Outline container, applies the metadata guard and starts the server.

Your server details are in /root/outline-vpn-server-credentials.txt, which only root can read. The outline.manager_config line in it is the administrator credential for your server, and anyone who has it can create and revoke access keys. Keep it private.

sudo cat /root/outline-vpn-server-credentials.txt
# Outline Server 1.12.3 (sac) - generated on this instance. KEEP PRIVATE: the manager line below is
# the administrator credential for this server (anyone who has it can create and revoke access keys).
# Paste the outline.manager_config value into Outline Manager (Add server > Advanced / 'Paste installation output').
# Compare the fingerprint Outline Manager shows with outline.cert_sha256. Guide: https://secureanycloud.com/stacks/outline-server-1-12-3/
# Ports: management API 60443 (TCP), access keys 60444 (TCP and UDP). outline.host=203.0.113.25 outline.cert_sha256=6A0E...(64 hexadecimal characters)...B91C outline.manager_config={"apiUrl":"https://203.0.113.25:60443/<secret path>","certSha256":"6A0E...B91C"}

The same two values are also in /opt/outline/access.txt, in the format that upstream's installer prints.

3

Confirm the server is running

Check that the three services that make up the server are active: Docker, the metadata guard and Outline itself.

systemctl is-active docker.service sac-outline-guard.service outline-vpn-server.service
active active active

Ask the server about itself through its management API. The helper reads the secret path for you and checks the server's key.

sudo outline-keys server
{ "name": "Outline Server (sac)", "version": "1.12.3", "serverId": "375114d2-1b4f-4c22-bf24-c9978f0716f2", "metricsEnabled": false, "portForNewAccessKeys": 60444, "hostnameForAccessKeys": "203.0.113.25" }

metricsEnabled: false is the anonymous metrics setting, which is off. hostnameForAccessKeys is the address every access key points at. A new server has no access keys:

sudo outline-keys list
[]

Confirm that only these ports are open on the instance: SSH, and the three ports Docker publishes for the container.

sudo ss -tulnp | grep -E ':(22|60443|60444) '
udp UNCONN 0 0 0.0.0.0:60444 0.0.0.0:* users:(("docker-proxy",...)) udp UNCONN 0 0 [::]:60444 [::]:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",...)) tcp LISTEN 0 4096 0.0.0.0:60444 0.0.0.0:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 0.0.0.0:60443 0.0.0.0:* users:(("docker-proxy",...)) ...
4

Run the self-test

outline-selftest checks the whole server with a real Shadowsocks client and a temporary access key that it creates and then revokes. Its two positive checks need outbound internet access. Run it:

sudo outline-selftest
ok: management API answers (key pinned): Outline Server 1.12.3, anonymous metrics sharing off ok: metadata guard loaded (DOCKER-USER drop 169.254.0.0/16 and 168.63.129.16) ok: temporary access key created (id 2) ok: internet egress through the proxy (TCP, checkip.amazonaws.com) answers: HTTP/1.1 200 ok: internet egress through the proxy (UDP, DNS to 1.1.1.1) answers ok: unreachable from a Shadowsocks client through the proxy: metadata 169.254.169.254, 168.63.129.16, loopback, this instance's private address (22, 60443), the Docker gateway ok: Amazon DNS 169.254.169.253 unreachable through the proxy (UDP) ok: metadata unreachable from inside the Outline container's network namespace (000) ok: the revoked temporary key no longer connects SELFTEST_OK
5

Add the server to Outline Manager

  • Install Outline Manager on your computer from https://getoutline.org/ and open it.
  • Choose to set up Outline on a server of your own ("Set up Outline anywhere"). Outline Manager shows the upstream install command. You do not need to run it, because the server is already installed.
  • Print the Outline Manager line on the instance, then paste it into the box that Outline Manager shows. Paste the whole {"apiUrl":...,"certSha256":...} value.
sudo outline-keys manager
  • Check the certificate fingerprint. Outline Manager trusts exactly the certificate whose fingerprint is in the line you pasted, so the certificate the server presents must match the fingerprint recorded on the instance:
test "$(echo | openssl s_client -connect 127.0.0.1:60443 2>/dev/null | openssl x509 -noout -sha256 -fingerprint | sed 's/.*=//; s/://g')" = "<OUTLINE_CERT_SHA256>" && echo "certificate fingerprint matches the credentials file"

Outline Manager can now rename the server, create and remove access keys, set data limits and show data usage. If it cannot reach the server, check that your security group allows TCP 60443 from your computer's address.

6

Create access keys

Create one access key per person or device. Use Add new key in Outline Manager, or run this on the instance:

sudo outline-keys add alice
id: 1 access key (share privately, it is a credential): ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNT...@203.0.113.25:60444/?outline=1

The ss:// link is that person's credential. Send it to them privately, for example with an end-to-end encrypted messenger. They paste it into the Outline Client app and choose Connect. Every key uses port 60444 on TCP and UDP, so that is the only port your users need to reach.

7

List and revoke access keys

List the keys. The command prints ids and names only, never the secrets.

sudo outline-keys list

Revoke a key by its id. The command checks that the key is gone and fails if it is not.

sudo outline-keys rm 1

A revoked key is refused for every new connection at once. To cut off a device that is connected right now, also restart the server with sudo systemctl restart outline-vpn-server.

8

Connect a device

On each device, install the Outline Client from https://getoutline.org/. Choose Add server and paste that device's ss:// access key.

While the device is connected, its traffic goes through your EC2 instance, so websites see your instance's public address.

9

Elastic IP or a DNS name

Access keys and the Outline Manager line contain the address the server had at first boot. If you attach an Elastic IP, or want keys to use a DNS name, point the server at it with this command:

sudo outline-keys hostname vpn.example.com

New and existing access keys then use that address. Users re-add their key in the Outline Client, or you send them the updated key from Outline Manager. The credentials file and the Outline Manager line are rewritten.

The TLS certificate and its fingerprint do not change, so Outline Manager keeps trusting the server.

10

Metadata access is blocked

outline-imds-guard shows the firewall rules that drop everything the Outline container sends to the metadata addresses, with packet counters. Run it:

sudo outline-imds-guard show
Chain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 0 0 DROP 0 -- * * 0.0.0.0/0 168.63.129.16 /* sac-outline-imds */ 0 0 DROP 0 -- * * 0.0.0.0/0 169.254.0.0/16 /* sac-outline-imds */ Chain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 0 0 DROP 0 -- * * ::/0 fd00:ec2::123 /* sac-outline-imds */ 0 0 DROP 0 -- * * ::/0 fd00:ec2::253 /* sac-outline-imds */ 0 0 DROP 0 -- * * ::/0 fd00:ec2::254 /* sac-outline-imds */

The Outline server cannot run without the guard. outline-vpn-server.service requires sac-outline-guard.service, and when Docker restarts, the guard is applied again before the server.

Docker, containerd and the Outline unit are also denied the metadata service directly:

systemctl show -p IPAddressDeny docker.service containerd.service outline-vpn-server.service
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32

Clients also cannot reach anything on your instance or in your VPC through the server. outline-ss-server refuses loopback, link-local and private (RFC 1918, CGNAT and IPv6 unique-local) destinations, and the self-test checks this (Step 4).

11

How the fail-closed start works, and starting over

outline-vpn-server.service starts only after first boot has written /var/lib/sac/data/outline-bootstrap-ready. Its preflight, /usr/local/sbin/outline-preflight, refuses to start the container unless these conditions hold:

  • The secret file is root-only.
  • The API secret and the end-entity certificate exist.
  • The metadata guard is loaded.
  • The pinned image is the one that was loaded.

The container receives its secrets through a root-only environment file, never on a command line.

To start over with a completely new server identity, run the command below. It creates a new management API secret and a new certificate and fingerprint, and it leaves no access keys, so every existing key stops working.

sudo outline-regenerate --yes

Then add the server to Outline Manager again with the new line from sudo outline-keys manager.

12

Updates

Ubuntu's unattended upgrades install security updates daily, from the Ubuntu archive and from Docker's repository. The image adds Docker to the allowed origins in /etc/apt/apt.conf.d/51sac-docker-unattended. When Docker is upgraded, the Outline server restarts with it, and the metadata guard is applied again first.

The Outline server software itself never changes automatically, because there is no Watchtower. Outline releases new server versions rarely. When SecureAnyCloud publishes a newer Outline version in this listing, you can launch the new version and recreate your keys, or keep running this one.

The image identity is recorded in /etc/outline/outline.conf and checked at every start.

13

Logs

The server's warnings and errors go to the journal. Informational messages are turned down, and the management API path is redacted. Read the latest entries:

sudo journalctl -u outline-vpn-server.service --no-pager -n 20

To follow first-boot progress, run sudo journalctl -u outline-vpn-server-firstboot.service.

14

Security recommendations

  • Restrict TCP 22 and TCP 60443 in the security group to your own address. Keep TCP and UDP 60444 open to your users.
  • Keep the Outline Manager line private; treat it like an administrator password.
  • Give each person their own access key, and revoke keys you no longer need (Step 7). Set data limits from Outline Manager if you need them.
  • Use an Elastic IP so keys survive a stop and start (Step 9).
  • Launch with HttpPutResponseHopLimit=1 (Step 1, Option B) for an extra metadata wall.
  • Keep unattended upgrades on, so Docker and the operating system stay patched.

Support and licensing

Outline Server and outline-ss-server are licensed under the Apache License 2.0. Prometheus is Apache-2.0 and Node.js is MIT-licensed. The rebuild recipe (Dockerfile, go.mod, go.sum) and the provenance of every component are in /usr/share/doc/sac-outline/. The source is available from the upstream projects (https://github.com/Jigsaw-Code/outline-server) and from SecureAnyCloud on request.

The server's IP-to-country and IP-to-ASN data is IP Geolocation by DB-IP (https://db-ip.com), licensed under Creative Commons Attribution 4.0. The container refreshes the free DB-IP Lite databases weekly.

SecureAnyCloud support covers deploying and operating this image. Contact sales@secureanycloud.com.

Outline and Jigsaw are trademarks of Google LLC. SecureAnyCloud is not affiliated with or endorsed by Google, Jigsaw or the Outline Foundation.

Deploy on AWS

Launch Outline Server 1.12.3 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS