Self-hosted personal portfolio with admin dashboard, project gallery, and service hub.
410
A self-hosted personal portfolio built for makers, welders, and fabricators. Post your projects with photos, YouTube embeds, and full markdown writeups. Everything is managed through a private admin dashboard — no external CMS, no cloud accounts, no subscriptions.
| Page | Public | Logged-in User | Admin |
|---|---|---|---|
| Home, Projects | ✓ | ✓ | ✓ |
| Hub | — | ✓ | ✓ |
| Dashboard | — | — | ✓ |
The recommended way to run Amethyst. No environment variables required.
docker run -d \
--name amethyst \
--restart always \
-p 3000:3000 \
-v amethyst-data:/data \
-v amethyst-uploads:/app/public/uploads \
rayderc/amethyst:latest
Then open http://your-server:3000/setup to create your admin account.
services:
amethyst:
image: rayderc/amethyst:latest
container_name: amethyst
restart: always
ports:
- 3000:3000
volumes:
- ./data:/data
- ./uploads:/app/public/uploads
docker compose up -d
All variables are optional. The app runs out of the box with no configuration.
| Variable | Default | Description |
|---|---|---|
SESSION_SECRET | auto-generated | Cookie encryption key (min 32 chars). Recommended in production — keeps sessions alive across container restarts. |
DATABASE_PATH | /data/amethyst.db | Path to the SQLite database inside the container. |
SESSION_COOKIE_SECURE | false | Set to true when running behind a TLS-terminating reverse proxy (HTTPS). |
services:
amethyst:
image: rayderc/amethyst:latest
container_name: amethyst
restart: always
ports:
- 3000:3000
environment:
- SESSION_SECRET=your_random_secret_string_at_least_32_chars
- SESSION_COOKIE_SECURE=true
volumes:
- ./data:/data
- ./uploads:/app/public/uploads
| Path | Contains |
|---|---|
/data | SQLite database (amethyst.db) |
/app/public/uploads | Uploaded images, served at /uploads/* |
Mount both as volumes so data survives container updates.
server {
listen 443 ssl;
server_name portfolio.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Set SESSION_COOKIE_SECURE=true when running behind HTTPS.
docker pull rayderc/amethyst:latest
docker compose down && docker compose up -d
Database and uploads are preserved in volumes — nothing is lost on update.
Prerequisites: Node.js 20+, npm
git clone https://github.com/RayderC/Amethyst.git
cd Amethyst
npm install
npm run dev
Open http://localhost:3000, then navigate to /setup to create your admin account.
npm run dev)/setup — create your admin account (only works before any user exists)/login| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router + Pages API Routes) |
| Language | TypeScript 5 |
| Database | SQLite via better-sqlite3 |
| Auth | iron-session (encrypted cookie sessions) |
| Markdown | react-markdown + remark-gfm |
| Styling | Custom CSS with CSS custom properties |
| Container | Docker (multi-platform: linux/amd64, linux/arm64) |
app/
components/ Shared UI — Navigation, ProjectCard, MarkdownEditor,
CircuitBackground, FooterAuth
dashboard/ Protected admin pages — projects, home editor, hub, users
hub/ Private service links page (any logged-in user)
projects/ Public project list and detail pages
page.tsx Home page (server-rendered, reads config from DB)
layout.tsx Root layout — circuit canvas, dynamic metadata
pages/api/ API routes (Next.js Pages Router)
login / logout / user / register / setup
projects/ CRUD for portfolio projects
sessions/ CRUD for hub service links
site-config Home page content
users/ User management
favicon Favicon proxy (direct fetch → Google fallback)
lib/
db.ts SQLite connection, schema migrations, helpers
session.ts iron-session config
siteConfig.ts Default site content (overridden by DB values)
url.ts URL normalization helpers
public/uploads/ Uploaded images (gitignored, mounted as Docker volume)
MIT
Content type
Image
Digest
sha256:74bb3b056…
Size
97.3 MB
Last updated
5 months ago
docker pull rayderc/amethyst