CodeCraft Chronicles

Bash: Managing Bookmarks with Linkding and Podman

Managing personal bookmarks efficiently is essential for maintaining a clean and organized workflow, especially when working across multiple devices or projects. To achieve this, I use Linkding, a self-hosted bookmark manager, running inside a Podman container. This setup provides a lightweight, secure, and containerized way to manage and export my bookmarks.

Why Self-Hosting Matters

Relying on third-party bookmark services or cloud sync platforms often means giving up control over your data. Many of these platforms are closed-source, can shut down without notice, or use your personal data for analytics or advertising. By self-hosting Linkding, I maintain complete ownership of my bookmarks, ensuring:

Self-hosting isn’t just about independence; it’s about digital resilience — the ability to control and preserve your own information over time.

Publishing My List

In addition to managing bookmarks privately, I like to share curated resources publicly. By exporting my bookmarks to a Markdown file using the Linkding API, I can push that file directly to GitHub, GitLab, or any static site generator.

This simple workflow turns a private bookmark collection into a public knowledge resource — a living, version-controlled list of tools, tutorials, and inspiration. Hosting the list on GitHub also makes it easy for others to discover new links, suggest additions via pull requests, or even fork the list for their own use.

Essentially, this setup transforms what used to be a personal browser feature into a shareable knowledge hub powered by open-source tools and automation.

Why Podman?

Podman is a daemonless container engine designed to be a drop-in replacement for Docker. It offers several key advantages:

Deploying Linkding with Podman

To deploy Linkding, I use the following podman run command:

podman run -d \
  --name linkding \
  -p 9090:9090 \
  -v linkding-data:/etc/linkding/data \
  ghcr.io/sissbruecker/linkding:latest

This command:

Creating the Superuser

After the container is up and running, I create an administrative account using:

podman exec -it linkding python manage.py createsuperuser

This command launches a shell inside the container and runs Django’s management command to create a superuser for the Linkding web interface.

Managing API Access

To interact with Linkding’s REST API, an API key is required. Once generated from the web interface, I store it securely in my shell environment by adding the following line to my ~/.bashrc file:

export LINKDING_API_KEY="your_api_key"

After saving, I reload the configuration with:

source ~/.bashrc

This allows my export script to authenticate automatically when accessing the API.

Exporting Bookmarks to Markdown

To keep my bookmarks portable and readable, I wrote a custom bash script that exports and categorizes bookmarks into a Markdown file (README.md). The script fetches data from Linkding’s API, sorts bookmarks by predefined categories, and formats them for easy sharing.

Here’s the complete script:

#!/bin/bash

# Ordered category names
ordered_categories=(
  "Design"
  "Code"
  "Linux - Free Software / Services"
  "Hardware"
  "Analog & Retro"
)

# Tags per category
declare -A category_tags=(
  ["Design"]="design"
  ["Code"]="code"
  ["Linux - Free Software / Services"]="linux"
  ["Hardware"]="hardware"
  ["Analog & Retro"]="retro analog"
)

# ANSI colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Message dictionary
declare -A MSG=(
  [missing_api_key]="ERROR: Environment variable LINKDING_API_KEY is not set."
  [set_api_key]="Hint: export LINKDING_API_KEY='your_api_key'"
  [missing_jq]="ERROR: 'jq' is not installed or not available in PATH."
  [install_jq]="Hint: Install it with: sudo apt install jq"
  [downloading]="Downloading bookmarks..."
  [done]="README.md successfully generated with sorted categories."
  [header_main]="# My Bookmarks\n\nMy personal bookmark list.\n"
  [footer]="_Last updated: %s • Total bookmarks: %s _\n"
  [exported]="Exported: %s bookmark(s)"
)

# Check for API key
if [ -z "$LINKDING_API_KEY" ]; then
  echo -e "${RED}${MSG[missing_api_key]}${NC}"
  echo -e "${YELLOW}${MSG[set_api_key]}${NC}"
  exit 1
fi

# Check for jq
if ! command -v jq &> /dev/null; then
  echo -e "${RED}${MSG[missing_jq]}${NC}"
  echo -e "${YELLOW}${MSG[install_jq]}${NC}"
  exit 1
fi

# Download bookmarks
echo -e "${GREEN}${MSG[downloading]}${NC}"
curl -s -H "Authorization: Token $LINKDING_API_KEY" http://localhost:9090/api/bookmarks/ > bookmarks.json

# Input and output files
INPUT="bookmarks.json"
OUTPUT="README.md"

# Write main header
echo -e "${MSG[header_main]}" > "$OUTPUT"

# Process each category in defined order
for category in "${ordered_categories[@]}"; do
  tags="${category_tags[$category]}"
  jq_expr=$(echo "$tags" | awk '{for(i=1;i<=NF;i++) printf "any(.tag_names[]; . == \"%s\") or ", $i}')
  jq_expr="${jq_expr% or }"
  matches=$(jq -r ".results[] | select($jq_expr) | \"[\(.title)](\(.url)) - \(.description)\"" "$INPUT" | sort)
  if [ -n "$matches" ]; then
    echo "## $category" >> "$OUTPUT"
    echo "" >> "$OUTPUT"
    echo "$matches" | sed 's/^/- /' >> "$OUTPUT"
    echo "" >> "$OUTPUT"
  fi
done

# Add summary
count=$(jq '.count' "$INPUT")
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
footer_text=---\\n$(printf "${MSG[footer]}" "$timestamp" "$count")
echo -e "$footer_text" >> "$OUTPUT"
formatted_count="${GREEN}${count}${NC}"
echo -e "$(printf "${MSG[exported]}" "$formatted_count")"

How It Works

Example Output

# My Bookmarks

My personal bookmark list.

## Design

- [Luciano Pereira](https://github.com/lucianofedericopereira) - 


---
_Last updated: 2025-11-09 08:54:23 • Total bookmarks: 1 _

Conclusion

By combining Podman and Linkding, I have a secure, self-hosted solution for managing bookmarks that integrates seamlessly with my shell environment. The export script adds an extra layer of utility, allowing me to maintain a readable, version-controlled list of bookmarks in Markdown format — perfect for sharing or archiving.

Comments