Start
Revisit your travel memories
Open source travel documentation app for self-hosting or serverless usage
Lightweight and future proof
Try out the online version using example data or your own data.
Background
The idea for this application came to me while reflecting on the countless journeys I had taken. Despite all the places I had visited, I found myself forgetting key moments and locations. Friends would ask about my travels, and I often struggled to recall the details or find the photos that matched those memories. I realized I needed a way to not only document my trips but also retrieve the content of the documentation with ease.
That is how Immer in Bewegung was born. The name, meaning always in movement in German, perfectly encapsulates the heart of this project. To bring this concept to life, the frog is chosen as the mascot—a symbol of endless motion and adventure.
Note
You can check out the online app running at https://online.bewegung.app/. If you want to bind your own data, you can do so via https://online.bewegung.app/?path=source.
Basic idea
Over time, most code in applications becomes obsolete. To ensure your memories are not lost to such obsolescence, the core idea of this project is to visualize travel data stored in SQLite format. SQLite databases are easy to update using any of the many available tools, offer long-term reliability, and are more likely to remain compatible with future technologies. This makes it a solid, future-proof solution. However, scrolling through a database directly can be tedious—so why not build an interface on top? But this project is not just an application; it’s a versatile syntax that allows you to take travel notes in a simple and structured way.
Main features
- A single SQLite database file with a few easy-to-understand tables
- Use directly on GitHub or host it on your own server
- Use in conjunction with Immich to combine your documentation with photos (optional)
Made in Sweden
Quick Start
Usage Combinations
| Serverless usage | Self-hosting | |
|---|---|---|
| Configuration | Via settings in database file | Via settings in database file |
| Database | Your own database file that you bind via the browser File API | Your own database file that you bind via the browser File API or database file stored on a server |
Serverless usage (simplest choice)
Download the database template (read more about how to edit the file under Create the dataset
https://github.com/plans-coding/immer-in-bewegung/raw/refs/heads/main/personal/bewegung.sqlite
To bind your own SQLite file to the online application, go to
https://online.bewegung.app/?path=source
Afterwards just start browsing your trips at
Host on your own server
If you want your app available from many devices without need to bind it specifillay on each device, you can develop your instance to your own server instead.
Step 1 − Download files
Go to the directory where you want to place your files, e.g.
cd /var/www/iib
Download the latest release and extract to the folder
Step 2 − Edit settings
Specify your settings and the path to the database file in server_db_path.txt. Read more at Settings.
Create the dataset
Database Structure
| Table | Type | Description |
|---|---|---|
| bewa_Overview | Table | The table that defines the extent of your trips. One trip per row. |
| bewb_Events | Table | The table containg detailed notes from your trips. One row per day (date). |
| bewx_Settings | Table | Where the settings for the app is stored. |
For more detailed instructions, follow the Trip Syntax.
Tip
Download a pre-filled SQLite template file and edit the tables within.
Note
To obtain coordinates for a given position, you can use the Coordinate Tool and easily copy and paste the coordinates into the SQLite file.
Trip Syntax
Table: Overview
The first table in your SQLite file is named bewa_Overview with fields according to following.
Note
Regarding validation: Some fields are checked for syntax and must comply to that.
| Column | Description |
|---|---|
| InnerId | The InnerId is used to connect each row to date notes in table bewb_Events. It consists of a six characters long string generated randomly in Sqlite with lower(hex(randomblob(3)))Syntax: <xxxxxx> |
| ParticipantGroup | Could e.g. be Family, Private, or Work. Syntax: <string> |
| OuterId | The OuterID is exposed to end-user of the web app. Normally you can take the first letter of the TripDomain and the first letter of ParticipantGroup and add a chronological number. Syntax: <letters and dashes and serial numbers of your choice> |
| OverallDestination | Type in you trip primary destination. E.g. Finland, or Italia, Spain etc. Syntax: <string> |
| DepartureDate | Your departure date in ISO format. Syntax: YYYY-MM-DD |
| ReturnDate | Your return date in ISO format. Syntax: YYYY-MM-DD |
| TripLabels | Add keywords to the trip for filter purposes. Syntax: LabelA, LabelB, LabelC, ... |
| MapPins | The main stops of your trip that you want to plot on the map. Syntax (multi-line accepted): [ NAME_OF_MAP_PIN ]( LATITUDE, LONGITUDE ) |
| TripDescription | A short description that explain the aim of the trip. E.g. My fantastic summer trip to France. Syntax: <string> |
| PhotoStarttime | For use with Immich. If you leave your home at let say 8 pm and don’t want to include photos from earlier on departure day, then you can set a time here. If left empty, all photos from departure day will be included. Syntax: HH:MM |
| PhotoEndtime | For use with Immich. Same as above, but for return day. Syntax: HH:MM |
Table: Events
The second table in your SQLite file is named bewb_Events with fields according to following.
Note
Regarding validation: Some fields are checked for syntax and must comply to that.
| Column Name | Description |
|---|---|
| InnerId | Reference to corresponding InnerId in table bewa_Overview.Syntax: <xxxxxx> |
| Date | Your date in ISO format. Syntax: YYYY-MM-DD |
| Events | This is your fully description of what you did this day. Syntax (multi-line accepted): <string> |
| Accommodation | The name and the address of your accommodation. Syntax: <string> |
| AccommodationCountry | The name of the country you stayed in during the night. Syntax: <string> |
| AccommodationCoordinateAccuracy | Possibility to mark the accuracy of the coordinates. Syntax: <string> |
| AccommodationCoordinates | The gps coordinates of your accommodation, e.g. 59.329444, 18.068611. Syntax: lat_decimal,lng_decimal |
| TravelParticipants | The name of your travel buddies, separated by comma. N.B. Not exposured in web app. Syntax: <string> |
| AdditionalNotes | N.B. Additional notes not exposured in web app. Syntax (multi-line accepted): <string> |
| CountriesDuringDay | This field is for enhanced statistics. Enter all countries in cronologial order during that day, separated by comma. The country names need to conform to the country names defined in ContinentCountries. In front of the country name prefix *, **, and + is allowed. Read more under Special syntax used for Countries During Day (Events).Syntax (prefix allowed): <country name without space>, <country name without space>, <country name without space> [...] |
Special syntax used for Countries During Day (Events)
| Prefix | Function |
|---|---|
| * | Shorter visit of significant importance |
| ** | Very short visit of without significant importance |
| + | Restore, counts only if * and ** count |
Note
Countries During Day: When a prefix is used in front of a country name in field CountriesDuringDay in table
bewb_Eventsit affect the Statistics page.
Settings
Path to database
Change the relative path to the database file in server_db_path.txt if you want your own database to load automatically when self-hosting.
Database file
The settings is configured the database file table bewx_Settings. The following fields are applicable
| Attribute Group | Attribute | Value | Description |
|---|---|---|---|
| Base | HomeCountry | Country | Use this to remove your own country from the trip unique countries field. Use same spelling as in your notes and in the value of ContinentCountries. |
| Base | LanguageFile | Filename | Filename to the translation file located in the personal folder, e.g. swedish.json. |
| Definition | ContinentCountries | List | Definitions of continents and countries as well as their language (and spelling). |
| Definition | TripDomainColors | List | Color definitions of your trip domains. |
| Base | Immich | Disabled or Enabled | Enable if you want filter links from all event dates to Immich. |
| Photos | ImmichApiKey | Key | |
| Photos | ImmichCoverAlbumId | GUID | The GUID of the cover photo album in Immich. |
| Feature | ImmichUrl | URL | Online URL to the Immich installation (e.g. https://immich.example.com/). |
| Other | Dataset | Disabled or Enabled | If enabled, the link to Dataset is shown. |
| Other | ExternalMapProvider | URL | Set your prefered map provider. |
Trip Domains
There are three pre-defined trip domains
- Domestic trip
- Trip abroad
- Attachment trip
An attachment trip is defined as a visit to a country where you have a deeper connection. For example, if you study abroad, you might want to document that time but distinguish it from the ordinary abroad or domestic category.
Set theme colors to your different TripDomains.
Domestic:#0b5394
Abroad:#1d655e
Attatchment:#C60C30
Continent Countries
If you want to change (make other country definitions or translate country names) the pre-defined country settings you can do it by changing in the country definitions. Only countries defined in the table ContinentCountries can be used in your trip notes and only with the very exact spelling.
Tip
Other language: If you have the countries in
bewb_Events(CountriesDuringDay) written in another languange than English, then you can change the app behaviour by translating the countries inContinentCountriesto your own language.
Europe:Albania:AL
Europe:Andorra:AD
Europe:Belgium:BE
Europe:Bosnia-and-Herzegovina:BA
Europe:Bulgaria:BG
Europe:Cyprus:CY
Europe:Cyprus-Northern-Cyprus
Europe:Denmark:DK
Europe:Denmark-Faraoe-Islands:FO
Europe:Estonia:EE
Europe:Finland:FI
Europe:Finland-Åland:AX
Europe:France:FR
Europe:Georgia:GE
Europe:Greece:GR
Europe:Ireland:IE
Europe:Iceland:IS
Europe:Italy:IT
Europe:Kosovo:XK
Europe:Croatia:HR
Europe:Latvia:LV
Europe:Liechtenstein:LI
Europe:Lithuania:LT
Europe:Luxembourg:LU
Europe:Malta:MT
Europe:Moldova:MD
Europe:Moldova-Transnistria
Europe:Monaco:MC
Europe:Montenegro:ME
Europe:Netherlands:NL
Europe:North-Macedonia:MK
Europe:Norway:NO
Europe:Poland:PL
Europe:Portugal:PT
Europe:Romania:RO
Europe:Russia:RU
Europe:San-Marino:SM
Europe:Switzerland:CH
Europe:Serbia:RS
Europe:Slovakia:SK
Europe:Slovenia:SI
Europe:Spain:ES
Europe:United-Kingdom:GB
Europe:United-Kingdom-Akrotiri-and-Dhekelia:GB
Europe:United-Kingdom-Gibraltar:GI
Europe:United-Kingdom-Jersey:JE
Europe:United-Kingdom-Northern-Ireland:GB
Europe:Sweden:SE
Europe:Czech-Republic:CZ
Europe:Germany:DE
Europe:Ukraine:UA
Europe:Hungary:HU
Europe:Vatican:VA
Europe:Belarus:BY
Europe:Austria:AT
Africa:Algeria:DZ
Africa:Angola:AO
Africa:Benin:BJ
Africa:Botswana:BW
Africa:Burkina-Faso:BF
Africa:Burundi:BI
Africa:Cabo-Verde:CV
Africa:Central-African-Republic:CF
Africa:Democratic-Republic-of-the-Congo:CD
Africa:Djibouti:DJ
Africa:Egypt:EG
Africa:Equatorial-Guinea:GQ
Africa:Ivory-Coast:CI
Africa:Eritrea:ER
Africa:Eswatini:SZ
Africa:Ethiopia:ET
Africa:Gabon:GA
Africa:Gambia:GM
Africa:Ghana:GH
Africa:Guinea:GN
Africa:Guinea-Bissau:GW
Africa:Cameroon:CM
Africa:Kenya:KE
Africa:Comoros:KM
Africa:Lesotho:LS
Africa:Liberia:LR
Africa:Libya:LY
Africa:Madagascar:MG
Africa:Malawi:MW
Africa:Mali:ML
Africa:Morocco:MA
Africa:Mauritania:MR
Africa:Mauritius:MU
Africa:Mozambique:MZ
Africa:Namibia:NA
Africa:Niger:NE
Africa:Nigeria:NG
Africa:Republic-of-the-Congo:CG
Africa:Rwanda:RW
Africa:Senegal:SN
Africa:Seychelles:SC
Africa:Sierra-Leone:SL
Africa:Somalia:SO
Africa:Sudan:SD
Africa:South-Africa:ZA
Africa:South-Sudan:SS
Africa:Sao-Tome-and-Principe:ST
Africa:Tanzania:TZ
Africa:Chad:TD
Africa:Togo:TG
Africa:Tunisia:TN
Africa:Uganda:UG
Africa:Zambia:ZM
Africa:Zimbabwe:ZW
Asia:Afghanistan:AF
Asia:Armenia:AM
Asia:Azerbaijan:AZ
Asia:Bahrain:BH
Asia:Bangladesh:BD
Asia:Bhutan:BT
Asia:Brunei:BN
Asia:Cyprus:CY
Asia:Cyprus-Northern-Cyprus
Asia:Philippines:PH
Asia:United-Arab-Emirates:AE
Asia:Georgia:GE
Asia:India:IN
Asia:Indonesia:ID
Asia:Iraq:IQ
Asia:Iran:IR
Asia:Israel:IL
Asia:Japan:JP
Asia:Yemen:YE
Asia:Jordan:JO
Asia:Cambodia:KH
Asia:Kazakhstan:KZ
Asia:China:CN
Asia:Kyrgyzstan:KG
Asia:Kuwait:KW
Asia:Laos:LA
Asia:Lebanon:LB
Asia:Malaysia:MY
Asia:Maldives:MV
Asia:Mongolia:MN
Asia:Myanmar:MM
Asia:Nepal:NP
Asia:North-Korea:KP
Asia:Oman:OM
Asia:Pakistan:PK
Asia:Qatar:QA
Asia:Russia:RU
Asia:Saudi-Arabia:SA
Asia:Singapore:SG
Asia:Sri-Lanka:LK
Asia:South-Korea:KR
Asia:Syria:SY
Asia:Tajikistan:TJ
Asia:Taiwan:TW
Asia:Thailand:TH
Asia:Timor-Leste:TL
Asia:Turkey:TR
Asia:Turkmenistan:TM
Asia:Uzbekistan:UZ
Asia:Vietnam:VN
North-America:Antigua-and-Barbuda:AG
North-America:Bahamas:BS
North-America:Barbados:BB
North-America:Belize:BZ
North-America:Costa-Rica:CR
North-America:Denmark-Greenland:GL
North-America:Dominica:DM
North-America:Dominican-Republic:DO
North-America:El-Salvador:SV
North-America:Grenada:GD
North-America:Guatemala:GT
North-America:Haiti:HT
North-America:Honduras:HN
North-America:Jamaica:JM
North-America:Canada:CA
North-America:Cuba:CU
North-America:Mexico:MX
North-America:Nicaragua:NI
North-America:Panama:PA
North-America:Saint-Kitts-and-Nevis:KN
North-America:Saint-Lucia:LC
North-America:Saint-Vincent-and-the-Grenadines:VC
North-America:Trinidad-and-Tobago:TT
North-America:USA:US
Oceania:Australia:AU
Oceania:Fiji:FJ
Oceania:Kiribati:KI
Oceania:Marshall-Islands:MH
Oceania:Micronesia:FM
Oceania:Nauru:NR
Oceania:New-Zealand:NZ
Oceania:Palau:PW
Oceania:Papua-New-Guinea:PG
Oceania:Solomon-Islands:SB
Oceania:Samoa:WS
Oceania:Tonga:TO
Oceania:Tuvalu:TV
Oceania:Vanuatu:VU
South-America:Argentina:AR
South-America:Bolivia:BO
South-America:Brazil:BR
South-America:Chile:CL
South-America:Colombia:CO
South-America:Ecuador:EC
South-America:Guyana:GY
South-America:Paraguay:PY
South-America:Peru:PE
South-America:Suriname:SR
South-America:Uruguay:UY
South-America:Venezuela:VE
Translations
App translation
If the app is not available in your language you can easily translate it by placing a file in the folder personal/ and change the value in settings table bewx_Settings.
Read more under Settings.
Continent and Country translations
The continent and countries can be translated, read more Continent Countries.
Trip Domain translation
The trip domains can be translated, read more Trip Domains.
Immich Integration
Hide Immich search bar
Tip
When Immer in Bewegung embeds photos in image view (see below), the search bar from Immich will be displayed unless you use small a css modification in your Immich settings.
Add
body:has(#search-chips:target) #asset-selection-app-bar,
body:has(#search-chips:target) #search-chips {
display:none;
}
body:has(#search-chips:target),
body:has(#search-chips:target) #search-content {
background-color: #000000;
}
to User Icon > Admininstration > Settings > Theme Settings: Custom CSS.

Usage of Immich API
To make use of Immich API you need to allow CORS, example snippet to add to /etc/caddy/Caddyfile
(cors) {
@cors_preflight{args[0]} method OPTIONS
@cors{args[0]} header Origin {args[0]}
handle @cors_preflight{args[0]} {
header {
Access-Control-Allow-Origin "{args[0]}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers *
Access-Control-Max-Age "3600"
defer
}
respond "" 204
}
handle @cors{args[0]} {
header {
Access-Control-Allow-Origin "{args[0]}"
Access-Control-Expose-Headers *
defer
}
}
}
immich.your-server-name.dedyn.io {
# Needed for API call
import cors https://immich.your-server-name.dedyn.io
# Needed for iframe embedding
header {
Cross-Origin-Embedder-Policy require-corp
Cross-Origin-Resource-Policy cross-origin
}
@options method OPTIONS
respond @options 204
reverse_proxy http://localhost:2283
}
Other needs
Expose web server to internet
Using Caddy this becomes easy. Deploy the server with
immich.your-server-name.dedyn.io {
# Generate password storing hash with: caddy hash-password
basic_auth {
# Username "iib", password "bewegung"
iib $2a$14$basROD3Y0cLE.VqXd.h89.akCQDKzhp6IH9ND2CRFyEICkMrKn3AO
}
root /var/www/iib/
}
Alternatives
- Add support for https
- E.g. via Let’s encrypt
- Use user authentication via
- Authelia, or
- Authentik, or
- Client certificates, or
- Port forwarding over SSH (e.g. using Termius on Android)
List over SQL Queries
You can browse the folder src/queries/ if you want to see what SQL queries the app is using. This can be useful if you want to make some analytics in another other software.
Logotype
If you like Immer in Bewegung and want to link to the project from your homepage, you can use this button.
Code for embedding to copy
<a href="https://bewegung.app/" style="text-decoration:none;"><img src="img/get_bewegung.svg"></a>