It was September 2014, and I needed to procrastinate a University exam. Looking for something I could mentally categorize under “useful”, I happened upon Taskwarrior, and it has been running my life ever since.
In this post, I will describe what Taskwarrior is and how I’m using it, as well as my migration from Taskwarrior 2.6 and Taskd to the new Taskwarrior 3.4 and taskchampion-sync-server.
In short, Taskwarrior is a command line task management system.
Before I go into more detail, allow me to go on a short tangent: Taskwarrior contains my first open source contribution, in the form of a bug report. Many years back in 2016, I found that TW was getting slower and slower while I was setting up a complex dependency graph for a larger project. Looking into the code, I found that the circular dependency check did not include a mechanism to make sure that a task wasn’t looked at multiple times. So it could happen that the same task and all of its dependencies were checked multiple times while checking whether a single task could be added with a given set of dependencies without creating a circular graph.
I reported the issue and did some more elaborate explanation, leading to this commit to the project. I’m even still in the AUTHORS file. 🙂 The original task even still exists here. The frustrating thing right now: I cannot remember whether I also provided the code to fix the implementation or if I just provided the bug report? Argh.
But enough personal Michael lore. To introduce you to what Taskwarrior looks like, accompany me for a little story.
A story
So let’s imagine you’ve just run the Friday evening host update on a Wednesday for a change and suddenly, your task management stops working. You were vaguely aware that there was a new major release, but you decided to ignore it - mucking around with your task management is scary!
But now, the time has come. You roll back the change, but you know that it’s finally time to take the plunge and update Taskwarrior to the new 3.0 version. As is so often the case, you start with making sure you don’t forget:
$ task add "Migrate Taskwarrior to 3.0" project:admin.tools.tw-demo +admin +tools +current +taskwarrior
Created task 1.
You then go and admire your handiwork:
task proj:admin.tools.tw-demo
Our first task.
And there we are, the project is basically half done already. 😁
To celebrate your accomplishment, you go and fetch another coffee. While you prepare
your artisinal brew garnered with herbs from a secret valley deep in the snow-crowned
heights of the Alps, you suddenly realize: You forgot to tag the task with
taskwarrior to make clear which tool it is about!
You remedy it with a quick flourish of confident keystrokes:
task 1 mod +taskwarrior
Modifying task 1 'Migrate Taskwarrior to 3.0'.
Modified 1 task.
A bit rattled by that oversight, you decide to take a closer look at the task to make sure you did not forget anything else:
task 1 info

More detailed info on the first task.
Your sense of accomplishment not quite satisfied yet, you decide to create some more tasks:
task add "Migrate data on desktop" proj:admin.tools.tw-demo +taskwarrior +current +tools +admin +desktop
task add "Migrate data on laptop" proj:admin.tools.tw-demo +taskwarrior +current +tools +admin +laptop
task add "Deploy TaskChampion on k8s cluster" proj:homelab.services.tw +taskwarrior +current +homelab +services
task add "Do first TW sync from desktop" proj:admin.tools.tw-demo +taskwarrior +current +admin +tools +desktop
task add "Document TaskChampion setup" proj:homelab.docs +taskwarrior +current +homelab +docs
Slowly stroking the white cat impatiently waiting for you to return to the “Conquer a small country that can’t possibly defend itself” part of the evening, you take stock of your heroic labor:

Our current task list
You stare at the task list. Something is off. Something feels wrong. Like when the Homelab next to your desk suddenly sounds different because one of your Fediverse posts went Fedi-viral and all of the fans ramp up to cool down the queues. There it is: The documentation task really should not be at the bottom of the pile. Documentation is important!
You launch your favorite editor, Emacs, of course, and point it at ~/.taskrc.
You enter the following, to make sure that documentation tasks are always taken
seriously:
urgency.user.tag.docs.coefficient=+2
Now that’s looking better: Documentation is now the top priority.
But there’s still some visual distinguishing missing, so you head back to your terminal to have a look at TW’s colors:
task colors

Taskwarrior’s color documentation.
With a nice dark pink chosen, you open up the .taskrc file again and add the
following line:
color.tag.docs=rgb205

Task lists, now with more color!
So far, so good, you think to yourself. But you’re a technical lead in your secret other life. And you know the drill: A project is not properly setup before everything is put into a, preferably acyclic, dependency graph.
task 6 mod dep:4
task 3 mod dep:2,4,5
task 5 mod dep:4,2
task 1 mod dep:2,3,4,5
This ends up changing your task list to look like this: Tasks with dependencies set.
With that, only the two tasks which can actually be started are at the top, now clearly marked with a white background. All of the tasks which have unfinished dependency got a gray background.
Alright, that looks pretty acceptable. But there has to be more procrastination
material in here. Taking another look, you decide that you don’t really need
the individual tags of the tasks shown in your ’next’ report.
You use the task show command to grab the default value for the next report’s
columns:
task show report.next
report.next.columns id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency
report.next.context 1
report.next.description Most urgent tasks
report.next.filter status:pending -WAITING limit:page
report.next.labels ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg
report.next.sort urgency-
Then you go back in to add the following lines to .taskrc:
report.next.columns=id,start.age,entry.age,depends,priority,project,recur,scheduled.countdown,due.relative,until.remaining,description,urgency
report.next.labels=ID,Active,Age,Deps,P,Project,Recur,S,Due,Until,Description,Urg
This makes for a neater format of the task list, emphasizing the task description: The task list after the tags column has been removed.
Better, but there’s still some clutter in the form of the tasks which you cannot work on right now, namely the ones currently blocked. That can be remedied by using a different Taskwarrior report:
task ready
This only shows the tasks which can currently be acted upon: Only showing actionable tasks.
But even this little detour has not quieted the siren song of procrastination. Casting about for more ways to put the actual execution of the project off, your well-trained brain offers up a rationalization: It’s the middle of the week! You cannot possibly put your fingers into your task management system in the middle of a work week. A small misstep would wreak havoc upon your routine.
And so you decide to put the project off until Sunday. For good measure, you also put Sunday as the due date, just to make sure your brain doesn’t try to weasel out again:
task 2,4 mod wait:sunday due:sunday
And with that, the task ready report is blissfully empty. Definitely high time
for an eight year Lagavulin after this much highly productive work.
Come Sunday, the tasks appear in the list again, and you start with the first one:
task 2 start

Starting the first task.
Once you finished the task and migrated the desktop TW install to 3.0, you set the task to done:
task 2 done
Now, task ready only shows the k8s TaskChampion setup as still actionable. So
you get into your best robes, have The Acolyte bring in the big “How to Deploy a New Service in the k8s Cluster Without Bringing About The End of Creation” tome and begin to summon forth reams of YAML from the Void. You also sacrifice
a CPU to the kubectl, an ancient Pentium 1 no less - you really need this
project to go well.
It all goes well. The Acolyte breaths a sigh of relieve. He is weak. You knew you needed to replace him when he started babbling about how you hadn’t updated Kubernetes in almost two years. He just can’t seem to enjoy the sweet nectar of pressure, worry, anxiety and bliss that is the price to be paid for practicing the dark arts of YOLO.
After setting that task to done as well, the previously blocked documentation
and first sync tasks show up in New tasks became available.task ready now:
You decide that The Acolyte needs a bit of practice in forming full sentences and send him off to write the documentation on the new service deployment.
You yourself type a quick task sync, and lo and behold, it works. The tasks
are flowing off to the servers. Your task migration was successful. You finish
the remaining tasks in a haze of keystrokes, dark black coffees and pipe smoke.
Even The Acolyte finished his task, and he even used punctuation almost everywhere! You decide not to get rid of him quite so soon. Perhaps corrupting him into loving the eldritch joys of YOLO might be a fun pastime?
task ready
No matches.
Writer’s note
Okay, that was pretty amusing for myself. 😁 But for the deeper technical explanation I will return to my normal writing style. But I will definitely experiment a bit with getting The Acolyte into my posts. I have to figure out how to do that without obscuring the technical info with too much prose first, though. The above tour through Taskwarrior’s usage lend itself nicely to putting a bit of creative writing around it. Definitely nicer than my first dry “Type this, then type this, and if you now type this that happens” draft.
Urgency in Taskwarrior
A task’s urgency is arguably Taskwarrior’s core feature. It determines the ordering of tasks in a number of reports, and I’d like to talk a bit about it.
There are a lot of sources of both urgency increases and decreases in Taskwarrior, and they’re almost entirely configurable to fit your approach to task management.
Let’s have a look at one of my tasks, for the regular Homelab host updates. The
task info output contains a detailed table describing how the task’s urgency
is computed:

Example urgency table
The simplest values are coming from whether the task has certain attributes, getting 1 urgency from having at least one tag and another 1 from having a project set.
Then there’s the age of the task. This urgency value slowly converges towards 2 while the task’s age converges towards a configurable number of days. The default is 365 days, so when the task is 365 days old, it will get 2 urgency from age. This value won’t increase any more after that.
Urgency can also be reduced, for example when the task is currently waiting or when it is blocked. This makes sure that even when not filtering for actionable tasks, as I did in the examples above, the non-actionable tasks will appear behind actionable tasks in the list.
In addition to these mechanisms, you can also configure specific tags or certain projects granting an amount of urgency, to prioritize them. I will go into a bit more detail on my personal setup in the next section.
There is also an urgency change based on the due date, and how close that date is. Similar to the age-related urgency, a task’s urgency increases the closer it gets to its due date.
And finally, there’s also a helper for dependency hierarchies. By default, tasks which block other tasks get an urgency boost, but its a fixed value. But there’s also an option to increase the urgency of a blocking task by an amount of urgency depending on the urgency of the tasks it blocks.
How I’m using Taskwarrior
As TW is a highly configurable tool, I think it’s useful to show you how I’m working with it.
To start with, here is my My task stats over the last 11 years.task stats output:
I think the average time spend pending for a task, 5 months, shows a bit about my usage. I usually don’t just add tasks for the next week or so, but I tend to do a planning phase for larger projects and then add pretty specific tasks for everything that needs to be done. And then I’m slowly working my way through them.
For task creation, I’ve got two modes. This is as good a place as any to mention that while Taskwarrior is a great CLI tool, there was never any Android app I liked. I tend to have relatively deep project structures and quite a few tags on my tasks. And I haven’t seen any good Taskwarrior app which makes entering tasks even remotely as comfortable and fast as entering tasks on the terminal with autocomplete for tags and projects. So when I need to add some task on the go, I just jot it down in my notes app and enter it into TW the next time I’m in front of a terminal.
But back to new tasks. Most of the time, I’m entering them “complete” already,
setting the right project and tags when creating them. Sometimes, when I just
want to jot something down, I use the capture tag. It’s configured like
this:
urgency.user.tag.capture.coefficient=8.0
color.tag.capture=rgb405
This puts it pretty high on the task list and colors it in pink, which I don’t
use for any other coloring. This way, I will see the task the next time I’m
running task and can then take care of setting the project and tags and
dependencies properly.
I have several systems for prioritizing tasks.
The most important one consists of the three tags current, following, soon. They
are configured like this:
color.tag.soon=rgb150
color.tag.current=rgb035
color.tag.following=rgb043
urgency.user.tag.current.coefficient=+9
urgency.user.tag.following.coefficient=+6
urgency.user.tag.soon.coefficient=+3
These three tags, with current having blue font color, following being dark
green and soon being bright green, do a general categorization of tasks. current
is what I’m currently working on, the current project for example, or a blog
post I want to start. In task next output, there are only ever at most three
or four of those, with the rest of the current project’s tasks being dependent
on those actionable tasks and not seen in the task next output because they
get their urgency reduced by a lot.
following is then the category of tasks I’d like to start next. These might
be new Homelab projects for example. And soon is then the next-lower prio,
which I give to task I don’t want to do as the next project, but I also don’t
want to completely vanish into the morass of tasks which have none of the three
tags.
For categorization of tasks within one of those rough levels I’m using priorities, which I’m configuring like this:
urgency.uda.priority.H.coefficient=1.5
urgency.uda.priority.M.coefficient=0.0
urgency.uda.priority.L.coefficient=-0.5
Note how the three different urgency levels are each 3 apart from each other,
while the priorities here add 1.5 urgency at maximum. This allows me
to prioritize within an urgency level. E.g. even a soon tagged task with
priority:H won’t get more urgency than a following tagged task with pri:L.
Another little wrinkle I’ve added to my workflow is the short tag. It’s configured
like this:
urgency.user.tag.short.coefficient=1.4
Its intention is to highlight short tasks, which in my head is anything up to about 30 minutes. These are for when I’ve only got 30 minutes left before I need to do something else. Or for days where I want to feel really productive and want to close a whole bunch of tasks. 😁
When I’m starting to work on a task, I use the task start command, and task stop
when I stop working on it. This increases the urgency of the task so that it
shows up at the very top of the task next list and puts the task’s row in
the output onto an orange background. I use the mechanism to indicate which of
the current tasks I’m actively working on.
The feature also has a time journalling functionality, as each time task start
or task stop are called on a task, an annotation for the start/stop time is
added to the task. These annotations could theoretically be used to compute the
time spend on the task if you diligently run start/stop each time you start
or stop working on it. I’m not using the feature, as I don’t start/stop a task
only because I’m making dinner, for example. I’m using timewarrior
for time tracking.
As I’ve mentioned in the introduction, I’m using Taskwarrior for personal and work task, on the same database. To separate the two, I’m using Taskwarrior’s context feature. It’s configured like this:
context.work.read=project:work
context.work.write=
context.private.read=project.not:work or +home
context.private.write=
context=private
The context=private setting configures the context when running with this
.taskrc file to private. As you can see in the context.private.read filter,
this excludes the work project from all read operations. Meaning that when
I call task next on my private desktop or laptop, I will never get a work
task shown, and it’s the other way around at work, where I’ve set context=work
and don’t get any private tasks shown.
For completeness’ sake, here is my full .taskrc, with some annotations:
data.location=~/.task
include ~/.task/dark-256.theme
# Mostly the default value, but I've switched it around a bit so that `due.today`
# appears before `overdue`. Otherwise, the coloring rules will mark tasks as
# overdue once their due-time is passed, which I generally don't want, because
# shorthands like due:tomorrow set the due time to "00:00" for the next day,
# so tasks would immediately show as overdue, instead of "due today".
rule.precedence.color=deleted,completed,active,keyword.,project.,due.today,overdue,scheduled,due,blocked,tag.,blocking,recurring,tagged,uda
# Enabling task's "hooks", although I'm not currently using any
hooks=1
# Configuring tab completion to complete all tags
complete.all.tags=1
list.all.projects=1
# Disabling purging, so my older completed/deleted tasks are kept for historical
# purposes.
purge.on-sync=0
# Some date formatting configs
dateformat=Y.M.D-H:N
dateformat.holiday=Y.M.D
dateformat.report=Y.M.D
dateformat.annotation=Y.M.D-H:N
weekstart=Monday
# Re-jigging the columns and column order in the "next" report a bit to fit
# my personal taste.
report.next.columns=id,project,priority,due,start.active,entry.age,urgency,description.count
report.next.labels=ID,Proj,Pri,Due,A,Age,Urg,Description
# Increasing urgency of "started" tasks
urgency.active.coefficient=6.0
# Setting urgency for annotations to 0, because whether a task has annotations
# or not doesn't matter.
urgency.annotations.coefficient=0.0
# Sharply reducing blocked task's urgency, so they go to the bottom of task lists
urgency.blocked.coefficient=-11.0
# Only very slightly increasing urgency of blocking tasks. I use dependencies a
# lot, and I've found that whether a task is blocking or not doesn't always matter
# to me, so they only get a small boost.
urgency.blocking.coefficient=0.5
# Prio configuration
urgency.uda.priority.H.coefficient=1.5
urgency.uda.priority.M.coefficient=0.0
urgency.uda.priority.L.coefficient=-0.5
urgency.user.tag.backlog.coefficient=-10.0
# Increasing bug's urgency a little bit, so errors get fixed before I start
# something new.
urgency.user.tag.bug.coefficient=2.0
urgency.user.tag.capture.coefficient=8.0
# Tag of FOSS contributions, which also get a small boost in prio
urgency.user.tag.contrib.coefficient=0.5
urgency.user.tag.current.coefficient=+9
# I also note down TV shows, movies and games I'd like to watch/play, but they
# should not show up on the "normal" task list. So they get a special tag with
# a reduced urgency.
urgency.user.tag.entertain.coefficient=-9.0
urgency.user.tag.following.coefficient=+6
# Domestic chores. I should really bite the bullet and get a housekeeper. If
# only that didn't feel so incredibly decadent.
urgency.user.tag.haushalt.coefficient=0.5
# Bills get a high urgency too, so they don't get lost in the sea of other tasks
urgency.user.tag.rechnung.coefficient=10.0
urgency.user.tag.short.coefficient=1.4
urgency.user.tag.soon.coefficient=+3
urgency.waiting.coefficient=-3.0
uda.priority.default=M
context.work.read=project:work
context.work.write=
context.private.read=project.not:work or +home
context.private.write=
context=private
One last comment I would like to make: Taskwarrior is very much a task management software, not a project planning tool. I use it together with Emacs' org mode. It does task management really well, but doesn’t really have note taking capabilities. There are annotations which can be added like this:
task 123 annot "Some note"
But these are really only intended for short notes. I only use them if I want to store relevant links to websites for example.
Import/Export
One last important feature to note is Taskwarrior’s import/export functionality. Your tasks are not walled off in Taskwarrior. I will talk a bit more about its storage formats when I talk about the migration to 3.0, but it has a nice feature for importing and exporting tasks in JSON format.
For example, let’s look at the task for this blog post:
task 1016 export
It results in this JSON:
[
{"id":1016,
"description":"Blog: Taskwarrior",
"entry":"20230519T165524Z",
"modified":"20251110T191504Z",
"priority":"M",
"project":"blog",
"start":"20251110T191504Z",
"status":"pending",
"uuid":"c3bd5a56-684a-48e8-b54e-fa0267f34247",
"annotations":[
{
"entry":"20251110T191504Z",
"description":"Started task"
}
],
"tags":["blog","current","tools"],
"depends":["4511952a-a314-4cae-9b92-9ef15b14a188","f771863d-a079-4c33-aeb3-71790ee7272d"],
"urgency":19
}
]
A JSON in similar format can be used to import tasks as well. So switching to Taskwarrior or switching away from Taskwarrior is doable, with a bit of scripting to translate the JSON to whatever format the previous/new tool supports.
The migration to Taskwarrior 3.0
In March 2024, Taskwarrior 3.0 was released. This happened after a pretty long phase of the project being dormant. That dormancy wasn’t actually a bad thing. It continued working perfectly fine through all of those years, with perhaps one release per year on average. It’s a command line tool, and it only ever had three dependencies: libc, libgnutls and libuuid.
The syncing was really the only issue, as it was a bit cumbersome, having been based on TLS client certificates, and the sync server implementation has had its last release in 2022, and no really regular releases before that. And that was potentially a publicly accessible server.
The main change in the 3.0 release was a change of the data storage backend and sync features. Both of those were rewritten in Rust.
The data storage backend was also changed from plain text files to an SQLite database. This is, at least from my PoV, a slight step back. I liked the fact that the data was just stored in text files. I was able to make use of that fact a while ago when I needed to fix an issue in the data files due to my home partition filling up and a task update only being written back partially. At least from my PoV, this wasn’t a performance problem. The previous file based implementation written in C++ was perfectly workable even with a task database of my size. The performance definitely got worse with the SQLite DB, especially at work, where our home directories are stored on NFS mounts.
The rewrite of the sync backend was a pure win, though. Where before it was raw TCP, it now became HTTP, meaning I was able to put the new sync server behind Traefik, and I didn’t have any issues anymore with reaching the instance in my Homelab from work, which I had done with a complicated SSH tunnel contraption because I couldn’t set up VPN access to my Homelab from the work machine.
The migration itself was also done very well. Not only is there a tool to migrate the TW v2 file-based storage to the v3 SQLite DB, but they also left the frontend entirely untouched, so my day-to-day usage was not impacted at all.
Migrating desktop data
My migration was from the 2.6.2 version all the way up to v3.4.1. This isn’t the latest release, there’s already v3.4.2, but that hasn’t made its way into the Gentoo packages yet.
My first step was seeing whether 3.4.1 even build in my work environment, as it wouldn’t have been useful to just migrate my personal use. And it did, due to its relatively few dependencies. Or well, its relatively few dependencies on the C++ side. Rust pulled in over a page of dependencies, of course.
With that verified, I backed up my local data and ran the migration script:
task import-v2
That went through without a hitch. Then I cautiously typed in task to show the
default next report. And then I waited. While it wasn’t really slow, there was
a measurable delay between me hitting enter and the report appearing.
I then tried a few other things, and while any read operations, like showing
reports, still were fast, any write operations were slow as molasses.
A simple task start 1536 took about a minute:
time task start 1536
Starting task 1536 'Taskwarrior: Update on Desktop and migrate data'.
Started 1 task.
You have more urgent tasks.
Project 'admin.tools' is 51% complete (13 of 27 tasks remaining).
real 0m54,508s
user 0m54,460s
sys 0m0,027s
That was not at all the perf I was used to. That command was instantaneous with
the older versions. After some digging, I luckily came upon this ticket,
which seemed to describe a similar perf issue, resolved by disabling the project
progress report at the end of the output. I tried that by adding the following
to my .taskrc:
# Removed "project" because the computation was exceedingly expensive
# Added context and removed filter to restore 2.6 behavior
verbose=blank,header,footnote,label,new-id,news,affected,edit,special,sync,override,recur,context
Notably, I removed the project entry from the verbose list. After that, the
performance returned to what I was used to. In the same step, I also removed the
filter value from the verbose list, as now every command was showing the
filter I had applied, which isn’t something I needed.
I also added this setting:
purge.on-sync=0
This is a setting to prevent automated purging of completed and deleted tasks. This is supposed to keep performance up by cleaning older tasks which shouldn’t be needed anymore. But to me, my older deleted and completed tasks are a slice of personal history I would like to keep. I’m for example working on a history series about my Homelab, from before I started this blog. And because that was also before I had any configs version controlled, my task list is the only record I have from before approximately 2020 when it comes to the Homelab.
With that done, I looked at the new sync setup.
Migrating sync
As I’ve said above, the sync backend was also changed. There are now multiple backend available, Google Cloud, AWS S3 and the taskchampion-sync-server. All options are described in the TW docs and the sync man page.
I initially thought to use S3, but the implementation seems to be geared towards using AWS S3, as there’s for example no config option for enabling path-style buckets or setting an endpoint URL. In addition, I figure if there are interesting new sync-related features, they will likely be implemented in the taskchampion-sync-server, so I went with that.
The server supports both, SQLite and Postgres. I decided to go with the SQLite option, because having a full Postgres cluster felt a bit like overkill.
The documentation for the sync server can be found here. It’s not a very complicated piece of software to set up, so I will spare you most of the YAML manifests.
But here is at least the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: tc-sync
spec:
replicas: 1
securityContext:
runAsNonRoot: true
selector:
matchLabels:
homelab/app: tc-sync
strategy:
type: "Recreate"
template:
metadata:
labels:
homelab/app: tc-sync
spec:
automountServiceAccountToken: false
securityContext:
fsGroup: 1000
containers:
- name: tc-sync
image: ghcr.io/gothenburgbitfactory/taskchampion-sync-server:{{ .Values.appVersion }}
volumeMounts:
- name: tc-data
mountPath: {{ .Values.mountDir }}
resources:
requests:
cpu: 200m
limits:
memory: 200Mi
env:
- name: DATA_DIR
value: "{{ .Values.mountDir }}"
- name: LISTEN
value: "0.0.0.0:{{ .Values.port }}"
- name: CLIENT_ID
value: "f5ea2f03-2f6b-4fc8-bae9-38fd06c08c65"
- name: CREATE_CLIENTS
value: "true"
- name: RUST_LOG
value: "info"
livenessProbe:
httpGet:
port: {{ .Values.port }}
path: "/"
initialDelaySeconds: 15
periodSeconds: 60
ports:
- name: tc-sync-http
containerPort: {{ .Values.port }}
protocol: TCP
volumes:
- name: tc-data
persistentVolumeClaim:
claimName: tc-sync-volume
One noteworthy piece of information is the CLIENT_ID list together with
the CREATE_CLIENTS environment variable. Clients are identified by their
UUID, created with the uuidgen tool. “Client” is a synonym for data set here,
so all clients using the same client ID will work on the same set of tasks.
The CREATE_CLIENTS variable controls whether new client IDs which have not
been seen before lead to new clients being created or are rejected as unknown.
If it is set to false, new clients need to be added to the database manually,
there is currently no tooling around that. But what’s possible is to keep the
variable set to true, and then setting CLIENT_ID to a comma-separated list of
allowed IDs. Then, only clients sending those IDs are automatically created when
first seen, and all other IDs are rejected.
Once the server was running, I only needed to add an encryption secret and the
server URL to my .taskrc file. The encryption secret is used for encrypting the
tasks before they get synced, so the server only ever sees encrypted tasks, which
is a very nice touch.
An example of the .taskrc settings would look like this:
sync.server.url=https://tw.example.com/
sync.server.client_id=f5ea2f03-2f6b-4fc8-bae9-38fd06c08c65
sync.encryption_secret=12345
The sync.server.client_id needs to be in the CLIENT_ID list of the server for
this setup to work. Otherwise the server will return a 403 HTTP code.
With this setup above, I started syncing and everything worked pretty nicely, I was able to sync to both my laptop and my work machine without issue.
But then I made a small mistake: I committed the above lines into my dotfiles
repository, including the sync.encryption_secret value. This was not good, of
course, as that value should be secret. I just deleted the sync server’s DB and
created a new UUID for good measure and tried syncing again. But nothing at all
happened, besides the client throwing this error message:
Failed to synchronize with server: Server Error: https://tw.example.com/v1/client/add-version/789 responded with 500 Internal Server Error
I then found that there was no way to reset the syncing on the client side. So I ended up going into the local SQLite DB and running this SQL command:
UPDATE operations
SET synced = 0
I tried this just because I saw that the operations table had a synced column
set to 1 for all entries.
And that solved the issue, and the local client started a completely fresh sync.
This was all honestly a bit scary, because I rely a lot on Taskwarrior to organize my life, but in the end the team really did a great job with the rewrite in 3.0, and besides the performance issues I haven’t hit any other issues in the one week I’ve now been using the new version.
I hope you enjoyed this post and have perhaps even found a new task management tool to try.
Just remember: Applying really fancy tagging and coloring schemes to your task management isn’t actually bringing your project forward. 😉