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", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:2019/metrics"]
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 environment:
48 # Server configuration
49 ATCR_HTTP_ADDR: :5000
50 ATCR_BASE_URL: https://${APPVIEW_DOMAIN:-atcr.io}
51 ATCR_SERVICE_NAME: ${APPVIEW_DOMAIN:-atcr.io}
52
53 # Storage configuration (derived from HOLD_DOMAIN)
54 ATCR_DEFAULT_HOLD_DID: ${ATCR_DEFAULT_HOLD_DID:-did:web:${HOLD_DOMAIN:-hold01.atcr.io}}
55
56 # Authentication
57 ATCR_AUTH_KEY_PATH: /var/lib/atcr/auth/private-key.pem
58 ATCR_AUTH_CERT_PATH: /var/lib/atcr/auth/private-key.crt
59 ATCR_TOKEN_EXPIRATION: ${ATCR_TOKEN_EXPIRATION:-300}
60
61 # UI configuration
62 ATCR_UI_ENABLED: ${ATCR_UI_ENABLED:-true}
63 ATCR_UI_DATABASE_PATH: /var/lib/atcr/ui.db
64
65 # Logging
66 ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-info}
67 ATCR_LOG_FORMATTER: ${ATCR_LOG_FORMATTER:-text}
68
69 # Jetstream configuration
70 JETSTREAM_URL: ${JETSTREAM_URL:-wss://jetstream2.us-west.bsky.network/subscribe}
71 ATCR_BACKFILL_ENABLED: ${ATCR_BACKFILL_ENABLED:-true}
72 ATCR_RELAY_ENDPOINT: ${ATCR_RELAY_ENDPOINT:-https://relay1.us-east.bsky.network}
73 ATCR_BACKFILL_INTERVAL: ${ATCR_BACKFILL_INTERVAL:-1h}
74 volumes:
75 # Persistent data: auth keys, UI database, OAuth tokens, Jetstream cache
76 - atcr-appview-data:/var/lib/atcr
77 networks:
78 - atcr-network
79 healthcheck:
80 test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/v2/"]
81 interval: 30s
82 timeout: 10s
83 retries: 3
84 start_period: 30s
85
86 atcr-hold:
87 build:
88 context: ..
89 dockerfile: Dockerfile.hold
90 image: atcr-hold:latest
91 container_name: atcr-hold
92 restart: unless-stopped
93 environment:
94 # Hold service configuration (derived from HOLD_DOMAIN)
95 HOLD_PUBLIC_URL: ${HOLD_PUBLIC_URL:-https://${HOLD_DOMAIN:-hold01.atcr.io}}
96 HOLD_SERVER_ADDR: :8080
97 HOLD_ALLOW_ALL_CREW: ${HOLD_ALLOW_ALL_CREW:-false}
98 HOLD_PUBLIC: ${HOLD_PUBLIC:-false}
99 HOLD_OWNER: ${HOLD_OWNER:-}
100 HOLD_BLUESKY_POSTS_ENABLED: ${HOLD_BLUESKY_POSTS_ENABLED:-true}
101
102 # Embedded PDS configuration
103 HOLD_DATABASE_DIR: ${HOLD_DATABASE_DIR:-/var/lib/atcr-hold}
104 # HOLD_KEY_PATH: ${HOLD_KEY_PATH} # Optional, defaults to {HOLD_DATABASE_DIR}/signing.key
105
106 # Storage driver
107 STORAGE_DRIVER: ${STORAGE_DRIVER:-s3}
108
109 # S3/UpCloud Object Storage configuration
110 AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
111 AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
112 AWS_REGION: ${AWS_REGION:-us-chi1}
113 S3_BUCKET: ${S3_BUCKET:-atcr-blobs}
114 S3_ENDPOINT: ${S3_ENDPOINT:-}
115 S3_REGION_ENDPOINT: ${S3_REGION_ENDPOINT:-}
116
117 # Logging
118 ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-debug}
119 ATCR_LOG_FORMATTER: ${ATCR_LOG_FORMATTER:-text}
120
121 # Optional: Filesystem storage (comment out S3 vars above)
122 # STORAGE_DRIVER: filesystem
123 # STORAGE_ROOT_DIR: /var/lib/atcr/hold
124 volumes:
125 # PDS data (carstore SQLite + signing keys)
126 - atcr-hold-data:/var/lib/atcr-hold
127 networks:
128 - atcr-network
129 healthcheck:
130 test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
131 interval: 30s
132 timeout: 10s
133 retries: 3
134 start_period: 30s
135
136networks:
137 atcr-network:
138 driver: bridge
139 ipam:
140 config:
141 - subnet: 172.29.0.0/24
142
143volumes:
144 caddy_data:
145 driver: local
146 caddy_config:
147 driver: local
148 atcr-appview-data:
149 driver: local
150 atcr-hold-data:
151 driver: local
152
153configs:
154 caddyfile:
155 content: |
156 # ATCR AppView - Main registry + web UI
157 ${APPVIEW_DOMAIN:-atcr.io} {
158 # Reverse proxy to AppView container
159 reverse_proxy atcr-appview:5000 {
160 # Preserve original host header
161 header_up Host {host}
162 header_up X-Real-IP {remote_host}
163 header_up X-Forwarded-For {remote_host}
164 header_up X-Forwarded-Proto {scheme}
165 }
166
167 # Enable compression
168 encode gzip
169
170 # Logging
171 log {
172 output file /data/logs/appview.log {
173 roll_size 100mb
174 roll_keep 10
175 }
176 }
177 }
178
179 # ATCR Hold Service - Storage presigned URL generator
180 ${HOLD_DOMAIN:-hold01.atcr.io} {
181 # Reverse proxy to Hold service container
182 reverse_proxy atcr-hold:8080 {
183 # Preserve original host header
184 header_up Host {host}
185 header_up X-Real-IP {remote_host}
186 header_up X-Forwarded-For {remote_host}
187 header_up X-Forwarded-Proto {scheme}
188 }
189
190 # Enable compression
191 encode gzip
192
193 # Logging
194 log {
195 output file /data/logs/hold.log {
196 roll_size 100mb
197 roll_keep 10
198 }
199 }
200 }