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-east-1}
113 S3_BUCKET: ${S3_BUCKET:-atcr-blobs}
114 S3_ENDPOINT: ${S3_ENDPOINT:-}
115
116 # Logging
117 ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-debug}
118 ATCR_LOG_FORMATTER: ${ATCR_LOG_FORMATTER:-text}
119
120 # Optional: Filesystem storage (comment out S3 vars above)
121 # STORAGE_DRIVER: filesystem
122 # STORAGE_ROOT_DIR: /var/lib/atcr/hold
123 volumes:
124 # PDS data (carstore SQLite + signing keys)
125 - atcr-hold-data:/var/lib/atcr-hold
126 networks:
127 - atcr-network
128 healthcheck:
129 test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
130 interval: 30s
131 timeout: 10s
132 retries: 3
133 start_period: 30s
134
135networks:
136 atcr-network:
137 driver: bridge
138 ipam:
139 config:
140 - subnet: 172.29.0.0/24
141
142volumes:
143 caddy_data:
144 driver: local
145 caddy_config:
146 driver: local
147 atcr-appview-data:
148 driver: local
149 atcr-hold-data:
150 driver: local
151
152configs:
153 caddyfile:
154 content: |
155 # ATCR AppView - Main registry + web UI
156 ${APPVIEW_DOMAIN:-atcr.io} {
157 # Reverse proxy to AppView container
158 reverse_proxy atcr-appview:5000 {
159 # Preserve original host header
160 header_up Host {host}
161 header_up X-Real-IP {remote_host}
162 }
163
164 # Enable compression
165 encode gzip
166
167 # Logging
168 log {
169 output file /data/logs/appview.log {
170 roll_size 100mb
171 roll_keep 10
172 }
173 }
174 }
175
176 # ATCR Hold Service - Storage presigned URL generator
177 ${HOLD_DOMAIN:-hold01.atcr.io} {
178 # Reverse proxy to Hold service container
179 reverse_proxy atcr-hold:8080 {
180 # Preserve original host header
181 header_up Host {host}
182 header_up X-Real-IP {remote_host}
183 }
184
185 # Enable compression
186 encode gzip
187
188 # Logging
189 log {
190 output file /data/logs/hold.log {
191 roll_size 100mb
192 roll_keep 10
193 }
194 }
195 }