A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
1# ATCR Production Deployment with Caddy
2# For UpCloud Rocky Linux deployment
3#
4# Usage:
5# 1. Copy .env.prod.template to .env and fill in your values
6# 2. docker compose -f deploy/docker-compose.prod.yml up -d
7#
8# Domains:
9# - atcr.io → AppView (registry API + web UI)
10# - hold01.atcr.io → Hold service (presigned URL generator)
11# - blobs.atcr.io → S3 object storage (CNAME to UpCloud S3)
12
13services:
14 caddy:
15 image: caddy:2-alpine
16 container_name: atcr-caddy
17 restart: unless-stopped
18 ports:
19 - "80:80"
20 - "443:443"
21 - "443:443/udp" # HTTP/3
22 environment:
23 APPVIEW_DOMAIN: ${APPVIEW_DOMAIN:-atcr.io}
24 HOLD_DOMAIN: ${HOLD_DOMAIN:-hold01.atcr.io}
25 volumes:
26 - caddy_data:/data
27 - caddy_config:/config
28 configs:
29 - source: caddyfile
30 target: /etc/caddy/Caddyfile
31 networks:
32 - atcr-network
33 healthcheck:
34 test: ["CMD", "caddy", "validate", "--config", "/etc/caddy/Caddyfile"]
35 interval: 30s
36 timeout: 10s
37 retries: 3
38 start_period: 10s
39
40 atcr-appview:
41 build:
42 context: ..
43 dockerfile: Dockerfile.appview
44 image: atcr-appview:latest
45 container_name: atcr-appview
46 restart: unless-stopped
47 command: ["serve", "--config", "/config.yaml"]
48 # Base config: config-appview.example.yaml
49 # Env vars below override config file values for this deployment
50 environment:
51 ATCR_BASE_URL: https://${APPVIEW_DOMAIN:-atcr.io}
52 ATCR_DEFAULT_HOLD_DID: ${ATCR_DEFAULT_HOLD_DID:-did:web:${HOLD_DOMAIN:-hold01.atcr.io}}
53 ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-info}
54 ATCR_LOG_FORMATTER: ${ATCR_LOG_FORMATTER:-text}
55 volumes:
56 - ./config-appview.yaml:/config.yaml:ro
57 # Persistent data: auth keys, UI database, OAuth tokens, Jetstream cache
58 - atcr-appview-data:/var/lib/atcr
59 networks:
60 - atcr-network
61 healthcheck:
62 test: ["CMD", "/healthcheck", "http://localhost:5000/health"]
63 interval: 30s
64 timeout: 10s
65 retries: 3
66 start_period: 30s
67
68 atcr-hold:
69 build:
70 context: ..
71 dockerfile: Dockerfile.hold
72 image: atcr-hold:latest
73 container_name: atcr-hold
74 restart: unless-stopped
75 command: ["serve", "--config", "/config.yaml"]
76 # Base config: config-hold.example.yaml
77 # Env vars below override config file values for this deployment
78 environment:
79 HOLD_PUBLIC_URL: ${HOLD_PUBLIC_URL:-https://${HOLD_DOMAIN:-hold01.atcr.io}}
80 HOLD_OWNER: ${HOLD_OWNER:-}
81 HOLD_BLUESKY_POSTS_ENABLED: ${HOLD_BLUESKY_POSTS_ENABLED:-true}
82 # S3/UpCloud Object Storage (REQUIRED)
83 AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
84 AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
85 AWS_REGION: ${AWS_REGION:-us-east-1}
86 S3_BUCKET: ${S3_BUCKET:-atcr-blobs}
87 S3_ENDPOINT: ${S3_ENDPOINT:-}
88 HOLD_LOG_LEVEL: ${ATCR_LOG_LEVEL:-info}
89 volumes:
90 - ./config-hold.yaml:/config.yaml:ro
91 # PDS data (carstore SQLite + signing keys)
92 - atcr-hold-data:/var/lib/atcr-hold
93 - ./quotas.yaml:/quotas.yaml:ro
94 networks:
95 - atcr-network
96 healthcheck:
97 test: ["CMD", "/healthcheck", "http://localhost:8080/xrpc/_health"]
98 interval: 30s
99 timeout: 10s
100 retries: 3
101 start_period: 30s
102
103networks:
104 atcr-network:
105 driver: bridge
106 ipam:
107 config:
108 - subnet: 172.29.0.0/24
109
110volumes:
111 caddy_data:
112 driver: local
113 caddy_config:
114 driver: local
115 atcr-appview-data:
116 driver: local
117 atcr-hold-data:
118 driver: local
119
120configs:
121 caddyfile:
122 content: |
123 # ATCR AppView - Main registry + web UI
124 ${APPVIEW_DOMAIN:-atcr.io} {
125 # Reverse proxy to AppView container
126 reverse_proxy atcr-appview:5000 {
127 # Preserve original host header
128 header_up Host {host}
129 header_up X-Real-IP {remote_host}
130 }
131
132 # Enable compression
133 encode gzip
134
135 # Logging
136 log {
137 output file /data/logs/appview.log {
138 roll_size 100mb
139 roll_keep 10
140 }
141 }
142 }
143
144 # ATCR Hold Service - Storage presigned URL generator
145 ${HOLD_DOMAIN:-hold01.atcr.io} {
146 # Reverse proxy to Hold service container
147 reverse_proxy atcr-hold:8080 {
148 # Preserve original host header
149 header_up Host {host}
150 header_up X-Real-IP {remote_host}
151 }
152
153 # Enable compression
154 encode gzip
155
156 # Logging
157 log {
158 output file /data/logs/hold.log {
159 roll_size 100mb
160 roll_keep 10
161 }
162 }
163 }