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.
- Home page — Editable hero, bio, and skills section. All content is changed from the dashboard with no code edits required.
- Projects — Full project list with tag-based filtering. Each project supports a thumbnail, photo gallery, YouTube embed, and a complete markdown writeup.
- Hub — A private bookmarks page for self-hosted services (Jellyfin, Nextcloud, Plex, Portainer, etc.) with auto-fetched favicons. Visible to any logged-in user.
- Animated cyberpunk UI — Circuit board background, neon glows, scan effects, and a glitch animation on the hero title.
- Fully responsive — Works on desktop and mobile. The admin dashboard has a slide-out drawer on small screens.
- Projects — Create, edit, and delete projects. Upload thumbnail and gallery photos directly (no external hosting needed). YouTube URL auto-parses into an embed.
- Home Page Editor — Change your name, badge text, hero lines, bio, skills list, email, and GitHub link. Updates the live site instantly.
- Hub Manager — Add, edit, and reorder links to self-hosted services. Favicon is auto-fetched from the service URL.
- Users — Create additional accounts (admin or regular user). Only admins can access the dashboard; regular users can only access the Hub.
| 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:latestThen 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/uploadsdocker compose up -dAll 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 -dDatabase 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 devOpen http://localhost:3000, then navigate to /setup to create your admin account.
- Start the app (Docker or
npm run dev) - Go to
/setup— create your admin account (only works before any user exists) - Log in at
/login - Dashboard → Home Page — set your name, bio, hero text, and skills
- Dashboard → Projects → New Project — add your first project
- Toggle "Feature on home page" to show it on the front page
- Dashboard → Hub — add links to your self-hosted services
| 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